直接列表初始化编译成功,但正常的直接初始化失败,为什么?

Jac*_*hin 6 c++ initialization overload-resolution

例如,代码如下:

struct A { A(int); };
struct B { B(A);   };

int main()
{
    B b{{0}}; // OK
    B c({0}); // error
}
Run Code Online (Sandbox Code Playgroud)

错误消息是:

f.cc: In function 'int main()':
f.cc:7:9: error: call of overloaded 'B(<brace-enclosed initializer list>)' is ambiguous
  B c({0}); // error

         ^
f.cc:7:9: note: candidates are:
f.cc:2:12: note: B::B(A)
 struct B { B(A);   };
        ^
f.cc:2:8: note: constexpr B::B(const B&)
 struct B { B(A);   };
        ^
f.cc:2:8: note: constexpr B::B(B&&)
Run Code Online (Sandbox Code Playgroud)

Col*_*mbo 8

从最新的官方标准C++ 14开始,您的第一次初始化并不含糊.[over.match.list]:

在此输入图像描述

由于不存在初始化列表构造函数,因此我们进入"第二阶段".现在考虑[over.best.ics]/4:

在此输入图像描述

我们的元素是{0}.因此,这不允许(用户定义的)转换{0}- > A用于复制构造函数.显然,如果我们不在[over.match.list]的第二阶段,这不适用,因此对于您的示例B c({0}),不会发生列表初始化,c并且考虑两个构造函数.


CWG第1467期

第一次初始化目前与第二次初始化一样模糊.编译器还没有实现CWG #1467 - 其解决方案已删除子弹点(4.5),如上所述.
请参阅#2076,它选择还原更改:

问题1467的解决使得一些看似合理的结构形成不良.例如,

struct A { A(int); };
struct B { B(A); };
B b{{0}};
Run Code Online (Sandbox Code Playgroud)

现在这是模棱两可的,因为B从13.3.3.1 [over.best.ics]第4段中删除了禁止用户定义的转换为复制和移动构造函数的文本.

"文本"是上述要点.理查德史密斯提出以下措辞:

对于非类类型,只有当列表中的元素本身不是列表时,我们才允许从单项列表初始化来执行复制(13.3.3.1.5 [over.ics.list]项目符号9.1).这种情况的类似规则是在13.3.3.1 [over.best.ics]第4段中加回子弹,但仅限于初始化程序本身是初始化程序列表的情况:

        13.3.1.7 [over.match.list]的第二阶段,当初始化列表只有一个元素本身就是一个初始化列表时,其中target是
        类的构造函数的第一个参数,X转换是X或引用(可能是cv合格的)X,

由于初始化{0}器本身就是一个初始化器列表,因此该子弹点将使您的第一次初始化再次成型.