【问题标题】:C++11: in-class initializaton with "= {}" doesn't work with explicit constructorC++11:使用“= {}”进行类内初始化不适用于显式构造函数
【发布时间】:2014-11-26 01:19:25
【问题描述】:

在 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 }; 但我个人觉得这很难向几十年来习惯使用= 表单的人解释,因为标准指的是“大括号或 - equal-initializer”,为什么旧​​的好方法在这种情况下不起作用并不明显。

【问题讨论】:

  • {} 初始化语法有点像 hack,它有一堆像这样的奇怪的极端情况
  • 我以为你必须使用双括号 - 试试Foo foo = { { 42 } };
  • @Matt McNabb:与其说{} 是一个黑客,不如说是使用= 并希望省略构造是一个黑客,但我个人更喜欢它的符号。无论如何,鉴于 省略是一种优化而不是保证,忽略 explicit 意味着您冒着额外的意外构造的风险。似乎要求代码明确反映该风险 - Foo foo = Foo{ 42 }; - 当构造函数标记为 explicit 时是合理的。冗长促使人们考虑和简化。虽然很乏味。
  • @TonyD 同意,但您可能对省略号有误。 According to Herb Sutter 省略实际上是有保证的。也就是说,我不知道他是基于什么断言的。
  • @KonradRudolph 嗯。是的,8.5 实际上让我走上了一条糟糕的道路:虽然它暗示(在我看来)Foo x={a}; 是复制初始化,但它实际上是 8.5.4 中涵盖的复制列表初始化。并且复制列表初始化与直接列表初始化相同,除非选择了 explicit 构造函数,否则它将被阻止。与Foo x=a; 不同,无论是否在逻辑上都不会创建临时文件。所以this works -- 阻止复制/移动,Foo a={x}; 样式,编译。没有{} 不会编译。

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


【解决方案1】:

我无法解释这背后的基本原理,但我可以重复显而易见的事情。

我觉得这很令人惊讶。真的是C++11的本意吗 这段代码不能编译的标准?

§13.3.1.7

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


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

Foo foo { 42 }direct initialization,而等号(带大括号)使它成为 copy-list-initialization。另一个答案的原因是,因为 copy-initialization (不带大括号的等号)的编译失败,所以复制列表初始化也失败也就不足为奇了,但这两个失败的原因不同。

cppreference:

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

还有他们在explicit specifier上的页面:

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

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

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

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

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

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

What could go wrong if copy-list-initialization allowed explicit constructors? 中所述,编译失败是因为选择了显式构造函数但不允许使用。

【讨论】:

    【解决方案2】:

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

    Foo foo = 42;
    

    所以对于“几十年来已经习惯了= 的表单的人”,{} 的表单也无法编译也就不足为奇了。

    【讨论】:

    • Foo foo = 42 隐式 创建一个Foo prvalue 来初始化fooFoo foo = {42} 不是这种情况,它 not 会隐式创建 Foo
    • 我支持@dyp:我明白为什么Foo foo = 42 不起作用,我可以接受。我仍然对为什么 Foo foo = { 42 } 不起作用感到困惑......从另一个答案看来,这可能是标准的意图或至少预期的,但似乎......不好。
    【解决方案3】:

    小部件 w = {x};

    这称为“复制列表初始化”。与widget w{x}的含义相同;除了不能使用显式构造函数。保证只调用一个构造函数。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-25
      • 1970-01-01
      • 2014-05-20
      • 2015-06-09
      相关资源
      最近更新 更多