我有这段代码:
#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?
答案 0 :(得分:1)
我最初给出的答案对于第一个引用是不正确的:尽管临时表通常在创建它们的完整表达式的末尾被销毁,但是如果引用必然会延长它们的生命周期临时或临时的子对象,除非有一些具体案例(根据12.2 [class.temporary]第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语句中的引用,并应用第三个项目:临时的生命周期不会超出表达式的结尾。此行为与不延长命名对象的生命周期一致,即使返回对这些对象的引用也是如此。