C++ 11:带有"= {}"的类内初始化不适用于显式构造函数

Joh*_*nck 18 c++ explicit-constructor initializer-list in-class-initialization c++11

在C++ 11中,我们可以使用"brace-or-equal-initializer"(标准中的单词)进行类内初始化,如下所示:

struct Foo
{
  /*explicit*/ Foo(int) {}
};

struct Bar
{
  Foo foo = { 42 };
};
Run Code Online (Sandbox Code Playgroud)

但如果我们不发表评论explicit,它就不再编译了.GCC 4.7和4.9说:

error: converting to ‘Foo’ from initializer list would use explicit constructor ‘Foo::Foo(int)’
Run Code Online (Sandbox Code Playgroud)

我发现这令人惊讶.C++ 11标准真的是这个代码无法编译的意图吗?

删除它=修复它:Foo foo { 42 };但我个人觉得更难以向那些已经习惯了这种形式的人解释=几十年,而且由于标准指的是"支撑或平等初始化器",因此很好的旧方式在这种情况下不起作用.

小智 12

我不能解释这背后的基本原理,但我可以重复这个显而易见的事实.

我发现这令人惊讶.C++ 11标准真的是这个代码无法编译的意图吗?

§13.3.1.7

在copy-list-initialization中,如果选择了显式构造函数,则初始化是错误的.


删除=修复它:Foo foo { 42 };但是我个人觉得更难以向那些习惯于使用=几十年的形式的人解释,并且因为标准指的是"支撑或平等初始化器",所以为什么好的不明显旧方法在这种情况下不起作用.

Foo foo { 42 }直接初始化,而等号(带大括号)使它成为复制列表初始化.另一个答案是因为编译因复制初始化而失败(没有大括号的等号),因此复制列表初始化失败也不足为奇,但两者因各种原因而失败.

cppreference:

直接初始化比复制初始化更宽松:复制初始化仅考虑非显式构造函数和用户定义的转换函数,而直接初始化则考虑所有构造函数和隐式转换序列.

他们在显式说明符上的页面:

指定构造函数和(自C++ 11以来)转换运算符,它们不允许隐式转换或复制初始化.

另一方面,对于copy-list-initialization:

T object = { arg1,arg2,... }; (10)

10)在等号的右侧(类似于复制初始化)

  • 否则,T的构造函数分为两个阶段:

    • 如果前一个阶段没有产生匹配,则T的所有构造函数都参与对由braced-init-list元素组成的参数集的重载解析,并限制只允许非缩小转换.如果此阶段产生显式构造函数作为复制列表初始化的最佳匹配,则编译失败(注意,在简单的复制初始化中,根本不考虑显式构造函数)

正如在copy-list-initialization允许显式构造函数允许的情况下所讨论的那样,编译失败,因为选择了显式构造函数但不允许使用.


Ant*_*vin 12

如果Foo(int)explicit,那么这也不会编译:

Foo foo = 42;
Run Code Online (Sandbox Code Playgroud)

因此,对于"已经习惯=了几十年的形式"的人来说,表单{}也不会编译也就不足为奇了.

  • `foo foo = 42`*隐式*创建一个`Foo` prvalue来初始化`foo`.对于`Foo foo = {42}`,情况并非如此,***隐式创建`Foo`. (5认同)

小智 7

小部件w = {x};

这称为"复制列表初始化".它与小部件w {x}相同; 除了不能使用显式构造函数.保证只调用一个构造函数.

来自http://herbsutter.com/2013/05/09/gotw-1-solution/

有关可以初始化对象的各种方法的详细讨论,请参阅本文的其余部分.