Overload resolution gets different result between gcc and clang

struct A { A(int);}; struct B { explicit B(A); B(const B&);}; B b({0}); 

gcc 5.1.0 gives an 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 completed successfully.

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

It may be like Direct List Initialization compiles successfully, but normal direct initialization fails, why? gcc and clang get the same result.

But that is another question. B(A) is explicit here. gcc and clang get different results.

+7
c ++ overloading c ++ 11
source share
2 answers

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 }); } 

GCC does not succeed, according to the standard (which states that explicit constructors are taken into account to initialize the list, so they can give ambiguity, but they simply cannot be selected). Klang accepts it and calls the second function.

In your case, what @Columbo says in his response to Direct List Initialization compiles successfully, but normal direct initialization fails, why? . With the difference that in your case B(const B&); it is no longer acceptable for the Clan, because the conversion {0} -> B would have to have two possibilities: an explicit constructor or recursively use the copy constructor a second time. The first option, as explained above, will not be considered by clang, and this time the @Columbo explanation is applied, and the copy constructor cannot be used a second time, because this will require a user-defined transformation, since we have one element (here, 0 ) Thus, in the summary, only the first constructor succeeds and is taken.


Since I understand that we are talking about strange rules for resolving congestion, and some may not be able to follow, here is a more intuitive explanation. Rules that are active in order

  • b({0}) means goto http://eel.is/c++draft/dcl.init#17 and from there to http://eel.is/c++draft/over.match.ctor , which is our first context is OR. Enumerated two constructors are B(A); and B(const B&) with argument {0} .

    • For B(A) it works with one user transformation.

    • For B(const B&) we need to initialize a const B& , which will lead us to http://eel.is/c++draft/over.ics.list#8 , then to http://eel.is/c+ + draft / over.ics.ref # 2 (using http://eel.is/c++draft/dcl.init#dcl.init.list-3 "Otherwise, if T is a reference type, the temporary value the type T refers to is an initialized copy list ... "), then http://eel.is/c++draft/over.best.ics#over.ics.list-6 . The resulting OR context has candidates B(A); and B(const B&) , with argument 0 . This is our second OR context and initialization of the copy list as per 13.3.1.7 (as required by over.ics.ref # 2 and dcl.init.list-3).

      • For B(A) constructor is explicit and therefore ignored by the Clan (in contradiction with the specification), but GCC is accepted (hence the ambiguity).

      • For B(const B&) this is a script processed by @Columbo, and therefore the user-requested conversion is prohibited. Newer projects no longer have this rule (but it will probably be added back). But since 0 to const B& will be a regular user-defined conversion (not list initialization), it will ignore the explicit constructor needed for the conversion anyway (for this potential second use of the copy constructor), and therefore a custom conversion would not be possible in any case, and the rule is much less important than what I thought when I wrote the brief summary above.

Therefore, for GCC, it can directly use an explicit constructor and, in addition, one use of the copy constructor. For clang, it takes into account directly the use of an explicit constructor and will not use it indirectly by initializing the copy list using a copy constructor such as GCC. Both will not consider using the copy constructor a second time, and that doesn't matter here.

+3
source share

The correct semantics of list initialization are

 B b{0}; 

which compiles fine. If you write B b({0}); , gcc cannot decide whether to directly call B(A) or create B ( {0} ) and then copy it with B(const B&) in the second phase. There is no priority order between these two parameters.

This is a language issue, not a compiler issue. See the gcc error report .

+2
source share

All Articles