【问题标题】:Initialization of const reference member in initializer list初始化列表中 const 引用成员的初始化
【发布时间】:2015-04-27 08:49:46
【问题描述】:

我在玩一些无用的代码来理解成员引用的初始化,结果碰到了这个:

struct A {};

struct B
{
    B() : a()
    {
    }

    const A& a;
};

上面的代码在使用 gcc 4.9.2 编译时出现以下错误:

In constructor 'B::B()':
error: value-initialization of reference type 'const A&'
  B() : a()

我明白了。

但是如果我在 B 的构造函数的初始化列表中使用统一初始化,像这样:

struct A {};

struct B
{
    B() : a{}
    {
    }

    const A& a;
};

它编译得很好。

那么问题来了,为什么这里使用统一初始化会改变编译结果呢?

我也在 Microsoft Visual C++ 2013 中尝试过。 它不会编译任一版本的代码,并显示相同的错误消息:

Error 3 error C2440: 'initializing' : cannot convert from 'int' to 'const A & 

你可以在这里快速玩一下:

http://ideone.com/7f2t8I

【问题讨论】:

  • 统一初始化是一个相对较新的特性,你必须预料到它会出现一些编译器错误。在这种情况下,不查,我猜微软的编译器是正确的。
  • 我的猜测:{} 被解释为对A 的默认构造函数的调用(就像你做了A a {}; 一样),所以创建了一个临时的A{}。然后它被绑定到 const-ref 并延长它的生命周期(就像你做了const A & a {},它也适用于 GCC)。但我不知道标准对此有何规定;如果允许的话……真正让我困惑的是,MSVC 看到了一个我看不到的int……你真的编译了这段代码吗?
  • @JamesKanze 我明白这一点,但我不想只是出去大喊编译器错误。也许有一些关于统一初始化的东西我不知道,这会使它在这种情况下表现不同。
  • @5gon12eder,这就是我的想法,直到我看到这个stackoverflow.com/questions/2784262/…。请参阅标准中的引述:在构造函数的 ctor-initializer (§12.6.2 [class.base.init]) 中临时绑定到引用成员会持续存在,直到构造函数退出。不完全确定如何解释。
  • @5gon12eder 标准过去常说引用的列表初始化总是创建一个临时的并且从不直接绑定。这就是链接问题中 GCC 问题的原因(当引用可以直接绑定时,创建一个临时的没有意义)并且已由 CWG1288 修复。临时生命周期问题是不同的。

标签: c++ gcc reference initialization initializer-list


【解决方案1】:

GCC 对{} 的解释是正确的。 [dcl.init.list]/p3.8-9(引用 N4296;较早的草案与这两个项目符号的相对顺序相同):

T 类型的对象或引用的列表初始化定义为 如下:

  • [省略7个不适用的项目符号]

  • 否则,如果T 是引用类型,则T 引用的类型的prvalue 临时被复制列表初始化或 直接列表初始化,取决于初始化的类型 引用,并且引用绑定到该临时文件。 [注意: 像往常一样,如果 引用类型是对非常量类型的左值引用。 —结束说明 ]

  • 否则,如果初始化列表中没有元素,则对象被值初始化。

列表初始化引用命中项目符号 3.8,导致临时构建。 3.9 中的值初始化案例不适用。

引用的值初始化格式不正确 ([dcl.init]/p9):

需要默认初始化的程序或 引用类型实体的值初始化格式不正确。


但是,从 N4296 开始,根据 [class.base.init]/p8:

绑定到一个引用成员的临时表达式 mem-initializer 格式不正确。

这是 CWG issue 1696 的结果,这是针对 C++14 的 DR(缺陷报告)。

CWG1696 之前的标准规定 (N4140 [class.temporary]/p5.1):

临时绑定到构造函数中的引用成员 ctor-initializer (12.6.2) 一直持续到构造函数退出。

这意味着引用将在构造后立即悬空。这大概促使 CWG1696 决定完全禁止此类绑定。

【讨论】:

  • 我假设 N4296 是 C++14。所以他们终于纠正了自 C++98 以来一直存在的问题(恕我直言)。
  • @JamesKanze N4140 是 C++14。 CWG 1696 是针对 C++14 的 DR,因此仍应由 C++14 模式下的编译器实现。
  • 是否有任何编译器具有完全兼容的 C++14 模式? (就此而言,是否有完全兼容的 C++11 模式?)
  • @JamesKanze 和 T.C.谢谢你们的cmets和答案。我现在很欣赏这两种初始化之间的区别。从这个答案中,我也相信 gcc 4.9.2 不符合有关 N4296 [class.base.init]/p8 的标准。
  • @Hugo 这是去年 11 月才添加的 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-09
  • 2011-07-14
  • 1970-01-01
  • 2015-09-25
  • 2015-02-07
  • 1970-01-01
相关资源
最近更新 更多