I have some code like the following:
class bar;
class foo
{
public:
operator bar() const;
};
class bar
{
public:
bar(const foo& foo);
};
void baz() {
foo f;
bar b = f; // [1]
const foo f2;
bar b2 = f2; // [2]
}
GCC gives an error at [2] but not [1]. Clang gives an error on both, and apparently MSVC gives an error on neither. Who's right?
Ambiguous. (Also, if you're stopping at the tl;dr, then the language-lawyer tag might not be your cup-of-tea. ^_^)
Both candidates have a single const foo& parameter, which binds equally to a const foo or foo argument. No other rules appear that would prefer one or the other function.
Breaking it down against the current C++ working draft
In both cases
T is the type being intialised, in both cases this is bar.
S is the type of the initializer expression, in the two cases foo and const foo respectively.
T are candidates ([over.match.copy]/1.1)
bar::bar(const foo& foo); is a candidate_cv_ S so non-explicit conversion functions are considered: ([over.match.copy]/1.2)
foo::operator bar() const is not hidden within foo or within const foo, and yields bar which is the same as T, and hence is a candidate.So our candidate list is the same in both cases:
bar::bar(const foo& foo)foo::operator bar() constIn both cases, we have a user-defined conversion consisting of:
If we select the constructor, the "result type" is "a prvalue of the cv-unqualified version of the destination type whose result object is initialized by the constructor" ([dcl.init]/17.6.3), so for both candidate functions, the second Standard Conversion is Identity (bar -> bar).
Per [dcl.init]/17.6.3, the initializer expression is going to be the argument to the selected call, in the two cases foo and const foo respectively.
bar::bar(const foo& foo)foo and const foo to const foo& ([over.match.viable]/4)const foo is reference-compatible with both foo and const foo as const is more cv-qualified than both const and nothing. [dcl.init.ref]/4const foo& binds directly to an lvalue foo and an lvalue const foo. [dcl.init.ref]/5foo::operator bar() constconst foo& in both cases ([over.match.funcs]/4)foo and const foo to const foo& ([over.match.viable]/4)Both are Identity => User Defined Conversion => Identity, i.e., are user-defined conversion sequences.
Can we establish a ranking between the sequences? Only if one of the following applies
Conversion sequences are indistinguishable, i.e., neither is better nor worse
Is either function a 'better' function? Only if one of the following applies
bar, but the other is not a constructor for a base class of bar ([over.match.best]/1.9)Neither is a 'better' function, so the call is ill-formed. [over.match.best]/2