在C ++ 11中,我们可以使用“brace-or-equal-initializer”(标准中的单词)进行类内初始化,如下所示:
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 };
但我个人觉得更难向那些已经习惯=
表格的人解释几十年了,而且标准是指一个“支撑或平等初始化器”并不明显为什么好的旧方法在这种情况下不起作用。
答案 0 :(得分:12)
我无法解释其背后的基本原理,但我可以重复这一点。
我发现这令人惊讶。它真的是C ++ 11的用意 此代码无法编译的标准?
§13.3.1.7
在copy-list-initialization中,如果选择了显式构造函数,则 初始化是不正确的。
删除=修复它:
Foo foo { 42 };
但我个人觉得这个 更难以向那些习惯于使用= for的形式的人解释 几十年了,而且标准是指一个 “支撑或平等初始化器”并不是很明显的原因 在这种情况下不起作用。
Foo foo { 42 }
为direct initialization,而等号(带括号)使其成为copy-list-initialization。另一个答案是因为copy-initialization编译失败(没有大括号的等号),因此复制列表初始化失败也不足为奇,但两者因各种原因而失败。
cppreference:
直接初始化比复制初始化更宽松: copy-initialization只考虑非显式构造函数和 用户定义的转换函数,直接初始化 考虑所有构造函数和隐式转换序列。
他们的页面在explicit specifier:
指定构造函数和(自C ++ 11以来)转换 不允许隐式转换的运算符或 副本初始化。
另一方面,对于copy-list-initialization:
T对象 = { arg1 , arg2 , ... }; (10)
10)在等号的右侧(类似于复制初始化)
否则,T的构造函数分为两个阶段:
- 如果前一个阶段没有产生匹配,则T的所有构造函数都参与对该参数集的重载解析 由带有限制的braced-init-list元素组成 只允许非缩小转换。 如果这个阶段 生成一个显式构造函数作为a的最佳匹配 copy-list-initialization,编译失败(注意,简单 复制初始化,根本不考虑显式构造函数)
如What could go wrong if copy-list-initialization allowed explicit constructors?中所述,编译失败,因为选择了显式构造函数但不允许使用它。
答案 1 :(得分:12)
如果Foo(int)
是explicit
,那么这也不会编译:
Foo foo = 42;
对于那些已经习惯了=
几十年的形式的人来说#34; {}
的表单也不会编译,这会让我感到惊讶。
答案 2 :(得分:7)
小部件w = {x};
这称为“复制列表初始化”。它与小部件w {x}相同;除了不能使用显式构造函数。保证只调用一个构造函数。
来自http://herbsutter.com/2013/05/09/gotw-1-solution/
有关初始化对象的各种方法的详细讨论,请参阅本文的其余部分。