为什么延长临时对象的寿命不会导致中间临时对象的延长
Why does extending the lifespan of a temporary object not result in the extension of the intermediate temporaries?
在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值。编译器的工作不是试图决定潜在的不可决定的决策问题,即使在人类显而易见的特殊情况下也是如此。
- 在不复制临时对象的情况下延长其生存期
- 为什么当我们有常量引用时创建临时对象?
- 程序如何'remember'临时对象?
- 返回对临时对象的引用
- 防止临时对象文件访问 MSVC 中的磁盘
- 是否可以在C++中移动临时对象的属性?
- 通过引用传递临时对象
- 临时C++对象是否为左值?
- 临时对象:术语澄清
- 存储对(可能)临时对象的引用是否合法,只要引用不比对象存活?
- 临时对象有身份吗?
- 临时对象上的运算符重载
- 如何在没有 std::move 的情况下移动临时对象
- 临时对象在C++中是不可避免的吗?
- 编译错误:临时对象构造函数中缺少参数
- 为什么在按值返回时创建临时对象,而不是在按值传递给函数参数时创建临时对象
- 我试图创建临时对象的方式有错误吗
- 子表达式中临时对象的生存期
- 对临时对象的Const引用不会延长其生存期
- 为什么延长临时对象的寿命不会导致中间临时对象的延长