2

I have this code:

#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;
}

Compile with -std=c++11 -O2 will output:

30
0

And a warning:

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

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

     cout << j.val << endl;

Is i.val a dangling reference? As far as I understand, the Int{30} temporary will be destroyed at the semi-colon, and i.val will be bound to the temporary's val which has been destroyed already. Is it correct?

And why the compiler says j is uninitialized, and j.val is 0?

4

1 回答 1

1

关于第一个引用,我最初给出的答案是不正确的:虽然临时对象通常在创建它们的完整表达式结束时被销毁,但如果引用绑定到临时对象或到临时的子对象,除非有一些特定情况(根据 12.2 [class.temporary] 第 5 段):

  1. 绑定到类的成员初始化器列表中的引用成员的临时对象在构造函数的末尾被销毁:编译器没有机会在不存储临时对象的情况下扩展临时对象的生命周期,因为它无法看到对象最终将存在的位置。
  2. 临时绑定到函数调用中的参数直到完整表达式结束(参数也是如此;在函数内绑定参数不会以任何方式延长临时生命周期)。
  3. 当在 return 语句中将临时对象绑定到引用时,返回的引用不会延长其生命周期,即,将返回过时的引用(返回引用的唯一用途是返回保存在其他地方且不在函数中的对象堆)。
  4. 新初始化程序中的临时绑定不会延长临时对象的生命周期,因为无法预测已创建对象的生命周期,并且需要在某个地方分配临时对象。

is no 子句禁止使用临时值直接初始化引用成员,只要它不使用初始化列表即可。此外,关于新初始化程序的项目示例实际上包含一个类似的示例(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 语句中的引用,并且第三个项目符号适用:临时对象的生命周期不会超出表达式的末尾。即使返回对这些对象的引用,此行为也与不延长命名对象的生命周期一致。

于 2013-11-08T22:39:51.800 回答