复制列表初始化的隐式转换的等级是多少 [英] What's the rank of implicitly conversion for copy-list-initialization

查看:109
本文介绍了复制列表初始化的隐式转换的等级是多少的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

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

 

对于给定的代码,候选函数为:

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

根据标准,对于#1,类型A的对象由{0}复制列表初始化为A a = {0}A::A(int)用于初始化,因此仅在#1内进行标准转换.对于#2,这是对引用表单braced-init-list的初始化,这是 [dcl.init.list]

否则,如果T是引用类型,则生成T引用的类型的prvalue. prvalue根据引用的初始化类型,通过copy-list-initialization或direct-list-initialization初始化其结果对象.然后使用prvalue直接初始化引用. [注:通常,如果引用类型是对非const类型的左值引用,则绑定将失败并且程序格式错误. —注释]

因此它等同于const B& = {0},在此初始化中,转换函数为B::B(A),参数为0,因此B tmp = {0}和'B :: B(A)'被视为参数已初始化通过参数0表示为A parameter = 0.

否则,(对于其余的复制初始化情况),用户定义的转换序列可以从源类型转换为目标类型,或者(使用转换功能时)转换为如[over.match.copy]中所述枚举其派生类,并通过重载分辨率选择最佳的类.

因此#2中存在用户定义的转换,并且#3的情况与#2相同,并导致解决方案

答案在这里,因为参数是初始化器列表,因此在解决重载时执行[over.ics.list]规则.

[over.ics.list]/6

  1. 否则,如果参数是非聚合类X ,并且每个[over.match.list]的重载分辨率选择X的最佳最佳构造函数C来执行类型的对象的初始化参数初始化程序列表中的X:
    • 如果C不是初始值设定项列表构造函数,并且初始值设定项列表具有cv U类型的单个元素,其中U是X或从X派生的类,则如果U是X,则隐式转换序列具有完全匹配等级,或者如果U是从X派生的,则转换排名.
    • 否则,隐式转换序列是用户定义的转换序列,第二个标准转换序列是身份转换.

因此,对于这三个候选构造函数,它们的参数均为非粗体类类型,因此隐式转换序列用于将构造函数列表的单个元素转换为构造函数参数的构造函数的对应参数类型B,因此这些转换序列都是用户定义的转换序列,而第二标准转换序列都是身份转换.因此,它们的排名是难以区分的.

#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&&)

解决方案

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.

这篇关于复制列表初始化的隐式转换的等级是多少的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆