首页 > 解决方案 > 复制列表初始化的隐式转换等级是多少

问题描述

#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 通过复制列表初始化或直接列表初始化来初始化其结果对象,具体取决于引用的初始化类型。然后使用纯右值直接初始化引用。[注意:像往常一样,如果引用类型是对非常量类型的左值引用,则绑定将失败并且程序格式错误。——尾注]

所以它等同于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.rank]

标准转换序列是比用户定义的转换序列或省略号转换序列更好的转换序列,并且...

标准转换比自定义转换好,所以#1应该比#2和#3好,但实际上,g++报告调用不明确,为什么?错误信息是:

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

标签: c++language-lawyeroverload-resolution

解决方案


所有三个转换{0} -> A, {0} -> const B&,{0} -> B&&都是用户定义的转换。

要转换{0}A,会发生另一个重载决议,这一次您面对三个构造函数A(int)A(const A&)并且A(A&&). 由于0 -> int是标准转换,而两者0 -> const A&都是0 -> A&&用户定义的转换,因此转换0 -> int获胜并被A(int)选择转换{0}A.

您的困惑来自混合两种重载解决方案。


推荐阅读