为什么延长临时对象的生命周期不会导致中间临时对象的扩展?

时间:2017-11-18 22:53:46

标签: c++ c++11

在C ++中,我们可以创建临时值,这些临时值具有生命周期。

来自cppreference.com

  

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

     

...

     
      
  • 临时对象的生命周期可以通过绑定到const左值引用或右值引用来扩展(自C ++ 11起),请参阅参考初始化以获取详细信息。
  •   

可以编写表达式,使得结果对象具有依赖的右值引用。可以通过分配非引用对象并将临时内容移入其中来删除这些依赖项,但由于额外的移动/副本,这将比仅使用临时对象效率低。

通过将具有相关临时对象的表达式作为函数参数插入,这将导致函数接收有效对象。这是因为表达式已成为完整表达式的子表达式。

但是,如果要延长由同一个表达式创建的对象的生命周期,那么表达式现在已成为完整表达式,所以我原本期望临时工可以在最坏的情况下继续使用在最好的情况下,情况或只是依赖的情况。然而,看起来所有的中间临时演员都被摧毁,导致一个临时的延长寿命与悬挂的引用/指针。

我相信这个问题会变得更加重要,因为我们有rvalue引用可供我们使用,而不仅仅是const引用。

所以我的问题是,为什么会这样呢?延长依赖性rvalues的寿命是否没有用例?或者这背后是否经过深思熟虑?

这是我的意思的一个例子:

#include <iostream>

struct Y
{
    Y()  { std::cout << " Y construct\n"; }
    ~Y() { std::cout << " Y destruct\n";  }
};

struct X
{
    Y&& y;
    X(Y&& y)
        : y( (std::cout << " X construct\n",
              std::move(y)) ) {}
    ~X() { std::cout << " X destruct\n"; }
    operator Y&() { return y; }
};

void use(Y& y)
{
    std::cout << " use\n";
}

int main()
{
    std::cout << "used within fn call\n";
    use(X(Y()));
    std::cout << "\nused via life extention\n";
    auto&& x = X(Y());
    use(x);
}

输出:

used within fn call
 Y construct
 X construct
 use
 X destruct
 Y destruct

used via life extention
 Y construct
 X construct
 Y destruct
 use
 X destruct

Demo

1 个答案:

答案 0 :(得分:2)

终身延长规则旨在:

  • 防止临时对象超出创建它们的范围;
  • 允许编译器静态确定何时发生生命周期扩展以及延长生存期何时结束。

如果不是这样,那么生命周期扩展会有运行时成本,其中指针或引用的每次初始化都必须增加与临时对象关联的引用计数。如果这是您想要的,请使用std::shared_ptr

在您的示例中,X::X(Y&&)构造函数可以在另一个翻译单元中定义,因此编译器甚至无法在翻译时告诉它存储对临时的引用传入。根据函数是在此翻译单元中定义还是在另一个单元中定义,程序的行为不应该有所不同。即使编译器可以看到X::X的定义,原则上X::y的初始化器可能是一个任意复杂的表达式,可能会或可能不会实际导致xvalue引用与参数{相同的对象{ {1}}。尝试决定潜在的不可判定的决策问题并不是编译器的工作,即使在对人类来说很明显的特殊情况下也是如此。