c++ 匿名结构中的常量引用成员变量是否会延长临时变量的生存期?

gcuhipw9  于 2023-01-22  发布在  其他
关注(0)|答案(2)|浏览(127)

请看下面的代码:

struct Temp{ int i = 0; };

Temp GetTemp() { return Temp{}; }

int main()
{
    for (struct{Temp const & t; int counter;} v = {GetTemp(), 0};
        v.counter < 10; 
        ++v.counter) 
    {
      // Is v.t guaranteed to not be a dangling reference?
      std::cout << v.t.i << std::endl;
    }
}

所以GetTemp()返回一个临时对象,然后赋值给一个常量引用变量,但是这个常量引用变量是一个匿名局部结构体的成员。C++标准是否保证临时变量的生存期延长到循环终止之后?
考虑到this question,我本以为答案是否定的,也就是说,我在循环体中得到了一个悬空引用,然而,gcc和clang似乎延长了生存期(参见example on godbolt),甚至没有抱怨-fsanitize=undefined,这让我感到惊讶。

pkmbmrz7

pkmbmrz71#

对于示例中的大括号聚合初始化,已保证since C++98的生存期延长(与类的链接/可见性属性无关)。这很直观,因为引用直接绑定到临时变量,而不是像你链接的问题那样通过一些中间的ctor参数。参见C14 FD中的[class.temporary],它概述了生存期扩展上下文。
另请参见这里的注解,它区分了C
20以来的花括号初始化和括号初始化:
[Note 7:与 direct-list-initialization 相比,收缩转换([dcl.init.list])是允许的,指示符是不允许的,绑定到引用的临时对象的生存期不会延长([class.temporary]),并且没有大括号省略。
[* 示例 * 3:

struct A {
    int a;
    int&& r;
};

int f();
int n = 10;

A a1{1, f()};                   // OK, lifetime is extended 
A a2(1, f());                   // well-formed, but dangling reference
niknxzdl

niknxzdl2#

匿名结构中的常量引用成员变量是否会延长临时变量的生存期?
是的,const引用链是完整的。只有vvfor循环结束之前是活动的,因此被引用的Temp的生存期被延长到那时。struct是匿名的这一事实没有影响。
class.temporary/4
有两种情况下临时变量会在完整表达式结束时被销毁。第一种情况是调用默认构造函数来初始化数组元素。如果构造函数有一个或多个默认参数,那么默认参数中创建的每个临时变量的销毁都将在构造下一个数组元素之前进行。
class.temporary/5
第二个上下文是当引用被绑定到一个临时对象时。引用所绑定到的临时对象或作为引用所绑定到的子对象的完整对象的临时对象在引用的生存期内持续存在除了:

  • (5.1)在构造函数的ctor-initializer([class. base. init])中临时绑定到引用成员的绑定会持续到构造函数退出。
  • (5.2)函数调用([www.example.com])中引用参数的临时绑定持续到包含该调用的完整表达式完成。expr.call]) persists until the completion of the full-expression containing the call.
  • (5.3)函数return语句([stmt. return])中返回值的临时绑定的生存期没有延长;临时变量在return语句中的完整表达式的末尾被销毁。
  • (5.4)新初始化式([www.example.com])中引用的临时绑定持续到包含新初始化式的完整表达式完成。expr.new]) persists until the completion of the full-expression containing the new-initializer.

生命期延长的例外情况均不适用于您的案例。

相关问题