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() const
In 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() const
const 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