为什么延长临时对象的寿命不会导致中间临时对象的延长

Why does extending the lifespan of a temporary object not result in the extension of the intermediate temporaries?

本文关键字:临时对象 中间 为什么      更新时间:2023-10-16

在C++中,我们可以创建临时值,这些临时值有一个寿命。

来自cppreference.com:

所有临时对象都将被销毁,作为评估完整表达式的最后一步,该表达式(词汇上)包含创建它们的点,如果创建了多个临时对象,则它们将按与创建顺序相反的顺序销毁。。。

  • 临时对象的生存期可以通过绑定到常量左值引用或右值引用来延长(从C++11开始),有关详细信息,请参阅引用初始化

可以编写一个表达式,使生成的对象具有依赖的右值引用。可以通过分配非引用对象并将临时对象的内容移动到其中来删除这些依赖项,但由于需要额外的移动/复制,这将比仅使用临时对象效率低。

通过插入这样一个带有依赖临时对象的表达式作为函数参数,这将导致函数接收到有效对象。这可能是因为表达式已经成为完整表达式的子表达式。

然而,如果要延长由同一表达式创建的对象的寿命,该表达式现在已经成为完整的表达式,所以我希望临时性在最坏的情况下与最后的临时性共存,或者在最好的情况下仅与依赖性共存。然而,似乎所有的中间临时性都只是被破坏了,导致了一个具有延长使用寿命的临时性,其中包含悬空引用/指针。

我相信,现在我们有了rvalue引用,而不仅仅是const引用,这个问题将变得更加令人难忘。

所以我的问题是,为什么会这样?是否没有延长依赖性价值寿命的用例?还是这背后有深思熟虑的想法?

下面是我的意思的一个例子:

#include <iostream>
struct Y
{
Y()  { std::cout << " Y constructn"; }
~Y() { std::cout << " Y destructn";  }
};
struct X
{
Y&& y;
X(Y&& y)
: y( (std::cout << " X constructn",
std::move(y)) ) {}
~X() { std::cout << " X destructn"; }
operator Y&() { return y; }
};
void use(Y& y)
{
std::cout << " usen";
}
int main()
{
std::cout << "used within fn calln";
use(X(Y()));
std::cout << "nused via life extentionn";
auto&& x = X(Y());
use(x);
}

输出:

在fn调用中使用Y构造X构造使用X析构函数Y析构函数通过延长寿命使用Y构造X构造Y析构函数使用X析构函数

演示

寿命扩展规则设计为:

  • 防止临时对象超过创建它们的范围
  • 允许编译器静态地确定寿命扩展何时发生以及扩展的寿命何时结束

如果不是这样,将存在运行时寿命扩展成本,指针或引用的每次初始化都必须增加与临时对象相关的引用计数。如果这是您想要的,请使用std::shared_ptr

在您的示例中,X::X(Y&&)构造函数可以在另一个翻译单元中定义,因此编译器甚至可能无法在翻译时告知它存储了对传入的临时的引用。程序的行为不应该因函数是在该翻译单元中还是在另一转换单元中定义而有所不同。即使编译器可以看到X::X的定义,原则上X::y的初始值设定项也可以是任意复杂的表达式,它可能会也可能不会实际导致引用与参数y相同对象的x值。编译器的工作不是试图决定潜在的不可决定的决策问题,即使在人类显而易见的特殊情况下也是如此。