长话短说
编译器无法延长 new A { "temporary " } 中涉及的临时文件的生命周期,因为创建的 A 和临时文件具有不同的存储期限。
可以在本文末尾找到对标准内容的参考。标准明确表示生命周期不会延长,但没有详细说明为什么会这样。
这篇文章将尝试以更广泛的受众可以理解的方式解释原因,而不是仅平均language-lawyer。
简介
在 C++ 中,一个对象可以有几种不同的存储持续时间,其中有自动-和动态存储持续时间,解释说简述如下:
自动存储时长
具有自动存储持续时间的对象的存储将持续到创建它们的块退出。
动态存储时长
具有动态存储持续时间的对象的存储将持续存在,直到明确声明它应该被释放;换句话说,这种存储不受任何特定范围的约束。
具有自动存储持续时间的聚合初始化
如上一节所述,临时具有自动存储期限。
如果我们用自动存储持续时间构造一个聚合,这也将存储绑定到当前范围;这意味着 temporary 的生命周期可以很容易地延长以匹配 aggregate 的生命周期。
注意:我们可以想象他们生活在同一个“盒子”中,在作用域结束时我们丢弃这个盒子 em>,这很好;无论是临时的还是 聚合,都不会超过 box 的生命周期。
我们的实施 (A)
struct A { std::string const& ref; };
void func () {
A x { {"hello world"} };
}
幕后 (A)
由于x和temporary都有自动存储持续时间,编译器可以实现如下功能,语义等价,sn-p:
void __func () {
std::string __unnamed_temporary { "hello world" };
A x { __unnamed_temporary };
}
注意:临时和聚合的生命周期都绑定到当前范围,太棒了!
聚合初始化与动态存储持续时间
我们的实施 (B)
A* gunc () {
A * ptr = new A { { "hello world" } };
return ptr;
}
int main () {
A * p = gunc ();
std::cout << p->ref << std::endl; // DANGER, WILL ROBINSON!
delete p;
}
在前面的部分中已经说过,临时对象具有自动存储持续时间,这意味着绑定到A::ref 的临时对象将在驻留在当前范围内的存储上构建。
幕后花絮 (B)
gunc 的语义等价可以如下实现:
A* gunc () {
A __unnamed_temporary { "hello world " };
A * ptr = new A { __unnamed_temporary }; // (1)
return ptr;
}
你也是这么想的,不是吗?
我们不能再延长临时文件的生命周期以匹配在 (1) 处使用 动态存储持续时间 创建的 A 的生命周期。
问题是__unnamed_temporary 的自动存储将在我们从gunc 返回后立即消失,从而有效地杀死我们的临时对象。
但是,动态创建的 A 仍然存在,给我们留下了 main 中的悬空引用。
结论
编译器无法延长通过 new-initializer 创建对象时涉及的任何临时对象的生命周期,因为 newed 对象和临时对象将具有不同的存储期限。
12.2p5 临时对象 [class.temporary]
引用绑定到的临时对象或作为引用绑定到的子对象的完整对象的临时对象在引用的生命周期内持续存在,但以下情况除外:
...