Ambiguity not picked up by compiler
Asked Answered
P

1

3

I had to spent some time in finding and fixing a bug that I managed to isolate in the following code:

#include <iostream>

struct A
{
    std::string S;
    A(const std::string s) { S = s; }
};

void f1(A a) { std::cout << "f1:a.S = " << a.S << "\n"; }
void f1(const std::string s) { std::cout << "f1:s = " << s << "\n"; }

void f2(A a) { std::cout << "f2:a.S = " << a.S << "\n"; }

int main()
{
    f1(A("test"));
    f1(std::string("test"));

    f2(A("test"));
    f2(std::string("test"));

    return 0;
}

The bug was caused by the overlooked (by me and the compiler(?)) ambiguity created by the f1-function: f2 clearly shows that both f1(A) and f1(std::string) apply to A, but when compiled the ambiguity is not picked-up by the compiler, and when executed the output is:

f1:a.S = test
f1:s = test
f2:a.S = test
f2:a.S = test

Is this behavior correct? Compiler problem? Or just plain old PIBCAK?

Plantation answered 25/11, 2012 at 19:27 Comment(1)
@Mat: Yes, the problem occurs when I call f1(std::string) expecting A and not getting it :(Plantation
A
8

The behavior that you describe is expected: there is no ambiguity. An overload resolution ambiguity occurs when two overloads match equally well and are both "the best overload."

When you call f1 with an argument of type A, the first f1 is an exact match; the second f1 does not match at all. Thus, f1 obviously wins during overload resolution.

When you call f1 with an argument of type std::string, the first f1 matches via the converting constructor of A; the second f1 is an exact match. The second f1 is a better match: it's an exact match and no conversion is required. The two overloads do not match equally well, thus there is no ambiguity. The second f1 wins during overload resolution.

Angeli answered 25/11, 2012 at 19:33 Comment(4)
"it's an exact match and no conversion is required": it's an exact match, yes, but a (standard) conversion needs to be done, in this case a qualification conversion.Vizcacha
@ipc: No qualification conversion is required. Top-level cv-qualifiers are not part of the type of a function and thus have no effect on overload resolution.Angeli
@slashmais: Overload resolution is fraught with subtleties.Angeli
The fact that a function's arguments' cv-qualifications are not part of its signature is because they aren't relevant to callers. The arguments are just local variables initialised during the call and only available within the body. So, one should never apply const-qualification to arguments in function declarations, only definitions - as this means you can change whether or not the argument is const in the latter without feeling obliged to update the former to match, forcing your users to recompile. (It makes no difference in terms of ABI, but make et al. don't take that into account)Hughey

© 2022 - 2024 — McMap. All rights reserved.