struct A { A(int);};
struct B { explicit B(A); B(const B&);};
B b({0});
gcc 5.1.0 gives the error
/dev/fd/63:3:8: error: call of overloaded 'B(<brace-enclosed initializer list>)'
is ambiguous
/dev/fd/63:3:8: note: candidates are:
/dev/fd/63:2:27: note: B::B(const B&)
/dev/fd/63:2:21: note: B::B(A)
while clang 3.6.0 succeeds.
Which one is right? Why?
For gcc 5.1.0: http://melpon.org/wandbox/permlink/pVe9eyXgu26NEX6X
For clang 3.6.0: http://melpon.org/wandbox/permlink/WOi1md2dc519SPW0
This may be similar to Direct list initialization compiles successfully, but normal direct initialization fails, why? which gcc and clang get same result.
But this is a different question. B(A)
is explicit here. gcc and clang get different results.
The difference can be reduced to
struct A { explicit A(int); };
struct B { B(int); };
void f(A);
void f(B);
int main() {
f({ 1 });
}
On GCC this fails, in accordance to the Standard (which says that for list initialization, explicit constructors are considered - so they can yield an ambiguity - but they just are not allowed to be selected). Clang accepts it and calls the second function.
In your case, what @Columbo says in his answer to Direct list initialization compiles successfully, but normal direct initialization fails, why? applies. With the difference that in your case, B(const B&);
is not anymore acceptable to Clang because the {0} -> B
conversion would be faced with two possibilities: the explicit constructor or using the copy constructor recursively a second time. The first option, as explained above, will not be considered by clang and this time the explanation by @Columbo applies and the copy constructor cannot be used a second time because that would need a user defined conversion as we have a single element (here, 0
). So in the summary, only the first constructor succeeds and is taken.
Since I understand the issue is about weird overload resolution rules and some might not be able to follow, here's a more intuitive explanation. The rules that are active are, in order
Therefore for GCC it can use the explicit constructor directly, and in addition by a single use of the copy constructor. For clang, it only considers using the explicit constructor directly, and it won't use it indirectly by a copy-list-initialization using the copy constructor like GCC does. Both won't consider using the copy constructor a second time, and it's irrelevant here.
The correct list initialization semantics is
B b{0};
which compiles fine. If you write B b({0});
, the gcc can't decide if call B(A)
directly or create B
({0}
) and then copy it with B(const B&)
in the second phase. There is no priority ordering between these two options.
It's language problem, not compiler's problem. See this gcc bug report.