悬挂参考右值

时间:2013-11-08 22:16:51

标签: c++ reference

我有这段代码:

#include <iostream>

using std::cout;
using std::endl;

struct Int {
    const int& val;
};

Int test(){
    return Int {30};
}

int main()
{
    Int i = Int{30};

    cout << i.val << endl;

    Int j = test();

    cout << j.val << endl;

    return 0;
}

编译-std=c++11 -O2将输出:

30
0

并发出警告:

main.cpp: In function 'int main()':

main.cpp:18:15: warning: '<anonymous>' is used uninitialized in this function [-Wuninitialized]

     cout << j.val << endl;

i.val悬挂参考?据我了解,Int{30}临时将在分号处被销毁,i.val将被绑定到已被销毁的临时val。这是对的吗?

为什么编译器说j未初始化,j.val为0?

1 个答案:

答案 0 :(得分:1)

我最初给出的答案对于第一个引用是不正确的:尽管临时表通常在创建它们的完整表达式的末尾被销毁,但是如果引用必然会延长它们的生命周期临时或临时的子对象,除非有一些具体案例(根据12.2 [class.temporary]第5段):

  1. 在构造函数的末尾销毁从类的成员初始化列表中的引用成员的临时绑定:编译器没有机会扩展临时生命而不存储临时,因为它无法看到对象最终的位置住。
  2. 函数调用中参数的临时绑定一直持续到完整表达式结束(参数也是如此;绑定函数内部的参数不会以任何方式延长临时生命周期)。
  3. 在return语句中将临时绑定到引用时,返回引用不会延长其生命周期,即返回过时引用(返回引用的唯一用途是返回其他地方保留的对象,而不是在函数的堆栈上。)
  4. 新初始值设定项中的临时绑定不会延长临时值的生命周期,因为无法预测创建对象的实时时间,并且需要在某处分配临时值。
  5. 没有子句禁止直接初始化具有临时值的引用成员,只要它不使用初始化列表。此外,关于新初始化程序的项目的示例实际上包含一个类似的示例(12.2 [class.temporary]第5段,第4章中的示例):

    struct S { int mi; const std::pair<int,int>& mp; };
    S a { 1, {2,3} };
    S* p = new S{ 1, {2,3} }; // Creates dangling reference
    

    该示例明确指出在第三行上初始化的引用是悬空但在第二行上没有这样做。这不是规范性文本,但它似乎表明第二行是正常的,上面提到的规则似乎也使该行合法。

    即声明

    Int i = Int{30};
    

    初始化i.val,并保留临时(int构建的30),直到i超出范围。另一方面,声明

    return Int {30};
    

    将临时绑定到return语句中的引用,并应用第三个项目:临时的生命周期不会超出表达式的结尾。此行为与不延长命名对象的生命周期一致,即使返回对这些对象的引用也是如此。