The program is ill-formed and rejected by GCC is correct here but the diagnosis can arguably say it is not completely correct. For this declaration B b(a);
, it is direct-initialization of an object of class B from the initializer a
of type A
, according to [over.match.copy] p1
Assuming that “cv1 T” is the type of the object being initialized, with T a class type, the candidate functions are selected as follows:
- The converting constructors of T are candidate functions.
- When the type of the initializer expression is a class type “cv S”, conversion functions are considered. The permissible types for non-explicit conversion functions are T and any class derived from T. When initializing a temporary object ([class.mem]) to be bound to the first parameter of a constructor where the parameter is of type “reference to cv2 T” and the constructor is called with a single argument in the context of direct-initialization of an object of type “cv3 T”, the permissible types for explicit conversion functions are the same; otherwise there are none.
For converting constructors, they are copy/move constructors of B
, however, [over.best.ics#general-4] prohibits the user-defined conversion sequence to apply to the target to match the parameter of the constructor
However, if the target is
- the first parameter of a constructor or
- [...]
and the constructor or user-defined conversion function is a candidate by
- [...]
- [over.match.copy], [over.match.conv], or [over.match.ref] (in all cases), or
- [...]
user-defined conversion sequences are not considered.
Hence, the copy/move constructors of B
are not viable functions. The ambiguity arises from the viable functions A::operator B()
and A::operator const B &()
, since the implicit parameter objects of them both have type A&
and the corresponding argument is an lvalue of type A
, hence neither is better than the other. Hence, the only opportunity that can determine which is better falls on [over.match.best#general-2.2]
the context is an initialization by user-defined conversion (see [dcl.init], [over.match.conv], and [over.match.ref]) and the standard conversion sequence from the return type of F1 to the destination type (i.e., the type of the entity being initialized) is a better conversion sequence than the standard conversion sequence from the return type of F2 to the destination type.
The second standard conversion sequences of them are both identity conversions, hence they are not indistinguishable. So, the result is ambiguity. GCC is merely correct in that the program is ambiguous, but, obviously, its diagnosis has a bit misleading. Since the copy/move constructors are not viable functions in this case at all, how could they cause the ambiguity? If we suppress the production of the defaulted move constructor, GCC and Clang are both incorrect here, which is back to this question you have referred.
const
qualifier on the return ofB&
overload inA
. – VievaB
the move constructor is deleted, so there is never an ambiguity as here. But I would agree that an answer on it will partially answer this question as well. – Capillaceous