After being confused why my code gave me an ambiguity error on GCC but no errors on Clang, I simplified the code. It can be seen below.
struct Foo
{
// Foo(F
To resolve the ambiguity, add explicit
to the conversion operator declaration:
struct Bar
{
template
explicit operator T()
{
return Foo{nullptr};
}
};
Why is it necessary? Because Foo
has a constructor taking a int*
, and so a operator int*()
instantiation of operator T()
template is being considered as part of overload resolution for the initialization of f
. See under [over.match.copy]:
1 [...] Assuming that “
cv1 T
” is the type of the object being initialized, withT
a class type, the candidate functions are selected as follows:
(1.1) The converting constructors of
T
are candidate functions.(1.2) When the type of the initializer expression is a class type “
cv S
”, the non-explicit conversion functions ofS
and its base classes are considered. 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 possibly cv-qualifiedT
” and the constructor is called with a single argument in the context of direct-initialization of an object of type “cv2 T
”, explicit conversion functions are also considered.
From (1.2) it follows that only implicit conversion functions are considered for the initialization, hence the ambiguity -- as the compiler cannot decide between constructing f
using a reference to Foo
or, as already mentioned, using a int*
(obtained by way of copy-initializing) from the return value of operator int*
. However, when the initializer expression is a temporary object, we also consider explicit conversions -- but only if they match a constructor taking a reference to Foo
, our "possibly cv-qualified T
", i.e. our copy and move constructors. This entire behavior is being consistent with [class.conv.fct¶2]:
A conversion function may be explicit ([dcl.fct.spec]), in which case it is only considered as a user-defined conversion for direct-initialization ([dcl.init]). Otherwise, user-defined conversions are not restricted to use in assignments and initializations.
And so, saying the same thing here for the 3rd time: if it isn't marked as explicit
, there's nothing stopping the compiler from trying to copy-initialize an int*
to be used for construction.