0
votes
#include <iostream>
struct A{
  A(int){

  }
};
struct B{
  B() = default;
  B(A){

  }
  B(B const&){}
  B(B&&){}
};

int main(){
  B b({0});
}

For the given codes, the candidate functions are:

 #1  B::B(A)   
 #2  B::B(const B&)  
 #3  B::B(B&&)  

According to the standard, for #1, the object of type A is copy-list-initialized by {0} as A a = {0}, A::A(int) is considered for the initialization, so only the standard conversion within #1. For #2, it's an initialization of a reference form braced-init-list which is the cause of [dcl.init.list]

Otherwise, if T is a reference type, a prvalue of the type referenced by T is generated. The prvalue initializes its result object by copy-list-initialization or direct-list-initialization, depending on the kind of initialization for the reference. The prvalue is then used to direct-initialize the reference. [ Note: As usual, the binding will fail and the program is ill-formed if the reference type is an lvalue reference to a non-const type.  — end note ]

So it equates with const B& = {0}, in this initialization, the conversion function is B::B(A) and the argument is 0, so B tmp = {0} and 'B::B(A)' is considered that parameter is initialized by argument 0, as A parameter = 0.

Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in [over.match.copy], and the best one is chosen through overload resolution...

So there's a user-defined conversion within #2 and the situation of #3 is the same as that of #2 and accroding to the [over.ics.rank],

a standard conversion sequence is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence, and...

The standard conversion is better than user-defined conversion, so #1 should be better than #2 and #3, but actually, g++ report the invocation is ambiguous, why? The error message is:

main.cpp: In function ‘int main()’:
main.cpp:12:10: error: call of overloaded ‘B(<brace-enclosed initializer list>)’ is ambiguous
   B b({0});
          ^
main.cpp:8:3: note: candidate: B::B(A)
   B(A){
   ^
main.cpp:6:8: note: candidate: constexpr B::B(const B&)
 struct B{
        ^
main.cpp:6:8: note: candidate: constexpr B::B(B&&)
2
All the three conversions {0} -> A, {0} -> const B&, {0} -> B&& are user-defined conversions. Why do you think {0} -> A is a standard conversion sequence?xskxzr
@xskxzr Becuase the standard [dcl.init.list],the constructor is considered to directly initialize the reuslt object,and covert 0 to int is identity conversion for A::A(int)xmh0511
@xskxzr or in other word,Do you think A a{0} is a conversion ?xmh0511

2 Answers

0
votes

All the three conversions {0} -> A, {0} -> const B&, {0} -> B&& are user-defined conversions.

To convert {0} to A, another overload resolution happens and this time you face three constructors A(int), A(const A&) and A(A&&). Since 0 -> int is a standard conversion while both 0 -> const A& and 0 -> A&& are user-defined conversion, the conversion 0 -> int wins and A(int) is selected to convert {0} to A.

Your confusion comes from mixing the two overload resolutions.

0
votes

Answers are here,Because the argument is initializer list,so [over.ics.list] rules are performed when overload resolution.

[over.ics.list]/6

  1. Otherwise, if the parameter is a non-aggregate class X and overload resolution per [over.match.list] chooses a single best constructor C of X to perform the initialization of an object of type X from the argument initializer list:
    • If C is not an initializer-list constructor and the initializer list has a single element of type cv U, where U is X or a class derived from X, the implicit conversion sequence has Exact Match rank if U is X, or Conversion rank if U is derived from X.
    • Otherwise, the implicit conversion sequence is a user-defined conversion sequence with the second standard conversion sequence an identity conversion.

Hence,for these three candiate constructors,the parameter of them are all non-aggerate class type,so the implicit conversion sequence is used to convert the single element of the initializer list to corresponding parameter type of the constructor of parameter of constructor B and therefore these conversion sequences are all user-defined conversion sequence and the second standard conversion sequence are all identity conversions.So the rank of them are indistinguishable.