C++ 扩展临时';的生存期,与块作用域聚合一起使用,但不通过“new”使用;为什么?

C++ 扩展临时';的生存期,与块作用域聚合一起使用,但不通过“new”使用;为什么?,c++,c++11,language-lawyer,object-lifetime,C++,C++11,Language Lawyer,Object Lifetime,注意:这个问题最初是由上提出的 //(1) A{“你好,世界”};//临时工的寿命延长到“a”的寿命` 标准::cout 长话短说编译器无法延长新A{“temporary”}中涉及的临时文件的生存期,因为创建的A和临时文件具有不同的存储持续时间。可以在本文末尾找到标准所述内容的参考。该标准明确规定生命周期不会延长,但没有详细说明原因。这篇文章将试图以一种更广泛的受众(而不仅仅是普通人)可以理解的方式解释原因 介绍 在C++中,对象可以有几种不同的存储持续时间,其中自动存储和动态存储持续时间

注意:这个问题最初是由上提出的


//(1)
A{“你好,世界”};//临时工的寿命延长到“a”的寿命`
标准::cout
长话短说

编译器无法延长
新A{“temporary”}
中涉及的临时文件的生存期,因为创建的
A
和临时文件具有不同的存储持续时间。

可以在本文末尾找到标准所述内容的参考。该标准明确规定生命周期不会延长,但没有详细说明原因。

这篇文章将试图以一种更广泛的受众(而不仅仅是普通人)可以理解的方式解释原因


介绍 <>在C++中,对象可以有几种不同的存储持续时间,其中自动存储和动态存储持续时间,下面简要解释:

自动存储持续时间

具有自动存储持续时间的对象的存储将持续,直到创建它们的块退出

  • 在块作用域中声明的对象具有自动存储持续时间(除非它们声明为
    静态
    外部
    ,而不是
    寄存器

  • 根据定义,临时表是在块范围内声明的,因此它们也具有自动存储持续时间


动态存储持续时间

具有动态存储持续时间的对象的存储将持续,直到明确声明应释放该对象为止;换句话说,这种存储不受任何特定范围的约束

  • 通过
    operator new
    动态创建的对象具有动态存储持续时间

    存储将一直保留,直到对
    operator delete
    进行匹配调用




具有自动存储持续时间的聚合初始化 如前一节所述,临时存储具有自动存储持续时间

如果我们构造一个具有自动存储持续时间的聚合,这也会将存储绑定到当前作用域;这意味着可以轻松地延长临时对象的生命周期,使其与聚合对象的生命周期相匹配

注意:我们可以想象他们生活在同一个“盒子”中,在范围结束时,我们丢弃这个盒子,这很好;临时和聚合都不会超过长方体的生存期


我们的实施(A)


幕后(A)

由于
x
和临时都具有自动存储持续时间,因此编译器可以按照以下语义等效的代码段实现该函数:

void  __func () {
  std::string __unnamed_temporary { "hello world" };
  A x { __unnamed_temporary };
}
注意:临时和聚合都将其生存期绑定到当前范围,太棒了



具有动态存储持续时间的聚合初始化 我们的实施(B)


你也在想,不是吗?

我们不能再延长临时存储的生存期,使其与使用动态存储持续时间创建的
A
的生存期匹配(1)

问题是,
\uu unnamed\u temporary
的自动存储将在我们从
gunc
返回后立即消失,从而有效地杀死我们的临时存储

然而,动态创建的
A
将仍然有效,在
main
中留下一个悬空的引用



结论 在通过新的初始值设定项创建对象时,编译器无法延长所涉及的任何临时对象的生存期,因为新对象和临时对象将具有不同的存储持续时间



标准()是怎么说的?
12.2p5
临时对象
[class.Temporary]

引用绑定到的临时对象或作为引用绑定到的子对象的完整对象的临时对象在引用的生存期内持续存在,但以下情况除外:

  • 临时绑定到新初始值设定项(5.3.4)中的引用将持续,直到包含新初始值设定项的完整表达式完成

    [注:这可能会引入悬空引用,在这种情况下,鼓励实现发出警告。--结束注]


相关报道:请落选的选民澄清一下为什么这个问题值得-1?@FilipRoséen refp:至少有一个人,活跃在标签中,他经常对任何自我回答的问题投否决票。我很清楚它是谁,这很烦人。解决方案是将
ref
更改为
std::unique_ptr ptr
并将其初始化为
新的{std::make_unique(“hello world”)?如果存在大量引用,则这对初始化每个引用都不起作用。@0x499602D2不会通过将对象绑定到引用来延长任何对象的生存期,但在代码段中不会有悬空引用/指针;因此,可以说这是一个解决方案。
// (1)

A a { "hello world" };              // temporary's lifetime is extended to that of `a`
std::cout << a.ref << std::endl;    // safe
// (2)

A * ptr = new A { "hello world" };  // lifetime of temporary not extended?
std::cout << ptr->ref << std::endl; // UB: dangling reference
struct A { std::string const& ref; };
void func () {
  A x { {"hello world"} };
}
void  __func () {
  std::string __unnamed_temporary { "hello world" };
  A x { __unnamed_temporary };
}
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* gunc () {
  A __unnamed_temporary { "hello world " };

  A * ptr = new A { __unnamed_temporary }; // (1)

  return ptr;
}