C ++ 11:使用“= {}”进行类内初始化不适用于显式构造函数

时间:2014-10-01 08:37:11

标签: c++ c++11 initializer-list explicit-constructor in-class-initialization

在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 };但我个人觉得更难向那些已经习惯=表格的人解释几十年了,而且标准是指一个“支撑或平等初始化器”并不明显为什么好的旧方法在这种情况下不起作用。

3 个答案:

答案 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/

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