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

C++11: in-class initializaton with "= {}" doesn't work with explicit constructor

本文关键字:不适用 初始化 适用于 构造函数 使用 C++11      更新时间:2023-10-16

在C++11中,我们可以使用"大括号或相等的初始化器"(标准中的单词)进行类内初始化,如下所示:

struct Foo
{
  /*explicit*/ Foo(int) {}
};
struct Bar
{
  Foo foo = { 42 };
};

但如果我们取消对explicit的注释,它将不再编译。GCC 4.7和4.9是这样说的:

error: converting to ‘Foo’ from initializer list would use explicit constructor ‘Foo::Foo(int)’

我觉得这很奇怪。这个代码不编译真的是C++11标准的意图吗?

删除=可以修复它:Foo foo { 42 };,但我个人发现这很难向几十年来习惯使用=的人解释,而且由于该标准指的是"大括号或相等的初始值设定项",所以不清楚为什么旧的好方法在这种情况下不起作用。

我无法解释这背后的原理,但我可以重复显而易见的内容。

我觉得这很奇怪。这真的是C++11的意图吗这个代码不编译的标准?

§13.3.1.7

在副本列表初始化中,如果选择了显式构造函数,则初始化格式不正确。


删除=修复了它:Foo foo { 42 };,但我个人发现更难向那些习惯了用=for的形式的人解释几十年,并且由于该标准指的是"brace or equal initializer"不清楚为什么采用旧的好方法在这种情况下不起作用。

Foo foo { 42 }是直接初始化,而等号(带大括号)使其成为复制列表初始化。另一个答案是,因为编译在副本初始化时失败(没有大括号的等号),所以它在副本列表初始化时也失败也就不足为奇了,但两者失败的原因不同。

cppreference:

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

他们在明确的说明符上的页面:

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

另一方面,对于复制列表初始化:

T对象={arg1arg2};(10)

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

  • 否则,在两个阶段中考虑T的构造函数:

    • 如果前一阶段没有产生匹配,则T的所有构造函数都会参与针对以下参数集的重载解析由支撑的init列表的元素组成,具有限制只允许非窄化转换如果这个阶段生成一个显式构造函数作为复制列表初始化,编译失败(注意,简单复制初始化,完全不考虑显式构造函数)

正如在"如果复制列表初始化允许显式构造函数,会出现什么问题?"中所讨论的那样?,编译失败,因为选择了显式构造函数,但不允许使用该构造函数。

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

Foo foo = 42;

因此,对于"几十年来一直习惯使用=的表单的人"来说,使用{}的表单也不会编译也就不足为奇了。

小部件w={x}

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

发件人http://herbsutter.com/2013/05/09/gotw-1-solution/

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