大括号的类型如何影响 C++ 中的对象生存期?

How can the type of braces influence object lifetime in C++?

提问人:Fedor 提问时间:7/10/2021 最后编辑:songyuanyaoFedor 更新时间:7/11/2021 访问量:1332

问:

我的一个朋友给我看了一个C++20程序:

#include <iostream>

struct A
{
    A() {std::cout << "A()\n";}
    ~A() {std::cout << "~A()\n";}
};

struct B
{
    const A &a;
};

int main()
{
    B x({});
    std::cout << "---\n";
    B y{{}};
    std::cout << "---\n";
    B z{A{}};
    std::cout << "---\n";
}

在 GCC 中,它打印:

A()
~A()
---
A()
---
A()
---
~A()
~A()

https://gcc.godbolt.org/z/ce3M3dPeo

因此,在情况 y 和 z 中,寿命延长。A

在 Visual Studio 中,结果是不同的:

A()
~A()
---
A()
---
A()
~A()
---
~A()

因此,只有在 y 的情况下,它的寿命才会延长。A

您能否解释一下为什么大括号的类型会影响对象的寿命?

C++ 初始化 C++20 生存期 临时对象

评论

2赞 Patrick Parker 7/10/2021
我认为这被称为未定义的行为。这意味着,对于这三者,您不应该假设 A 的生命周期延长了。但是,编译器可能会将 B 作为中间步骤进行优化,从而为您提供一个确实可以延长 A 生命周期的构造。
5赞 HolyBlackCat 7/10/2021
@PatrickParker 在某些情况下,可以保证延长使用寿命。即使不是,在读取/写入引用之前,它也不会是 UB。

答:

31赞 songyuanyao 7/10/2021 #1

GCC 是正确的。只有在聚合初始化中使用列表初始化语法(即使用大括号)时,临时的生存期才会延长

(从 C++20 开始)临时绑定到 引用元素中的引用 使用直接初始化语法(括号)初始化的聚合 与列表初始化语法相反,大括号一直存在到最后 包含初始值设定项的完整表达式。

struct A {
  int&& r;
};
A a1{7}; // OK, lifetime is extended
A a2(7); // well-formed, but dangling reference

对于直接初始化

(强调我的)

否则,如果目标类型是(可能是 CV 限定的)聚合类,则将按照聚合初始化中所述对其进行初始化,但允许缩小转换范围,不允许指定初始值设定项,与引用的临时绑定不会延长其生存期,没有大括号省略,并且任何没有初始值设定项的元素都将进行值初始化。(从 C++20 开始)

评论

6赞 HolyBlackCat 7/10/2021
IIRC 的意图(不延长生存期背后的意图)是模仿用户提供的构造函数的行为,这些构造函数无法延长生存期,因为它们间接初始化引用成员(来自引用参数),并且生存期扩展不会在初始化一个引用时传播另一个引用。()
0赞 Fedor 7/12/2021
上面的 z 案例呢?MSVC 有错吗?请注意,Clang 和 GCC 在类似情况下的行为也不同:gcc.godbolt.org/z/KP3c6cv8Y
1赞 songyuanyao 7/12/2021
@Fedor Lifetime 应该在 z-case 中延长,它主要与 y 大小写相同,我认为这是 MSVC 的错误。我不确定哪个值得提出新问题,但我倾向于认为 Clang 是对的;通常,临时的生存期不能通过“传递”来进一步延长:从临时绑定到的引用变量或数据成员初始化的第二个引用不会影响其生存期。B b{ B{ A{} } };
0赞 Fedor 7/12/2021
相关问题:stackoverflow.com/questions/68335315/...