正在通过const ref未定义行为捕获新构造的对象


11

是否可以执行以下(人为的示例)或未定义的行为:

// undefined behavior?
const auto& c = SomeClass{};

// use c in code later
const auto& v = c.GetSomeVariable();

Answers:


12

很安全 const ref延长了临时对象的寿命。范围将是const ref的范围。

可以通过绑定到const左值引用或右值引用(自C ++ 11起)来延长临时对象的生存期,有关详细信息,请参见 引用初始化

每当引用绑定到临时对象或其子对象时,临时对象的生存期都会延长以匹配引用的生存期,但以下情况除外

  • return语句中与函数的返回值绑定的临时绑定不会扩展:它在return表达式的结尾立即被销毁。此类函数始终返回悬空参考。
  • 构造函数初始化器列表中与引用成员的临时绑定仅在构造函数退出之前一直存在,而不管对象是否存在。(注意:从DR 1696开始,这种初始化是错误的)。
  • 在包含该函数调用的完整表达式的结尾之前,存在与该函数调用中的引用参数的临时绑定:如果该函数返回的引用超出了该完整表达式的引用,则它将变为悬空引用。
  • 直到在包含该new表达式的完整表达式的末尾为止,存在与new表达式中使用的初始化程序中的引用的临时绑定,时间与已初始化的对象一样长。如果初始化的对象的寿命超过了完整的表达式,则其引用成员将成为悬空引用。
  • 与使用列表初始化语法(括号)相反的,使用直接初始化语法(括号)进行初始化的聚合的引用元素中的引用的临时绑定存在,直到包含初始化程序的完整表达式的末尾为止。 struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference

通常,不能通过“传递”来进一步延长临时项的生存期:从临时项所绑定到的引用初始化的第二个引用不影响其生存期。

正如@Konrad Rudolph所指出的(并参见上面的最后一段):

“如果c.GetSomeVariable()返回对本地对象的引用或其本身正在延长某个对象的生存期的引用,则生存期扩展不会生效”


1
您应该引用该报价的来源。
Lightness Races in Orbit

@LightnessRaceswithMonica完成了。我一直在寻找更好的文字。
遗忘

2
最好强调这仅适用于价值观。如果c.GetSomeVariable()返回一个引用到本地对象或引用它本身就延长一些对象的生命周期,寿命延长并没有一命呜呼
康拉德·鲁道夫

@KonradRudolph谢谢!我也添加了例外。
遗忘


3

是的,这是绝对安全的:对const引用的绑定将临时项的生存期延长到该引用的范围。

注意,该行为不是传递的。例如,

const auto& cc = []{
    const auto& c = SomeClass{};
    return c;
}();

cc 晃来晃去。


2

这很安全。

[class.temporary]/5:在三种情况下,临时变量在与完整表达式结束时不同的时间点被销毁。[..]

[class.temporary]/6:第三个上下文是将引用绑定到临时对象时。如果绑定引用的glvalue是通过以下方式之一获得的,则引用绑定到的临时对象或作为引用绑定到的子对象的完整对象的临时对象在引用的生存期内将持续存在[这里有很多东西]


1

在这种情况下是安全的。但是请注意,并非所有临时对象都可以通过const引用安全捕获...例如

#include <stdio.h>

struct Foo {
    int member;

    Foo() : member(0) {
        printf("Constructor\n");
    }

    ~Foo() {
        printf("Destructor\n");
    }

    const Foo& method() const {
        return *this;
    }
};

int main() {
    {
        const Foo& x = Foo{};        // safe
        printf("here!\n");
    }
    {
        const int& y = Foo{}.member; // safe too (special rule for this)
        printf("here (2)!\n");
    }
    {
        const Foo& z = Foo{}.method(); // NOT safe
        printf("here (3)!\n");
    }
    return 0;
}

为之获得的引用z不是安全使用的,因为临时实例将在到达printf语句之前在完整表达式的末尾销毁。输出为:

Constructor
here!
Destructor
Constructor
here (2)!
Destructor
Constructor
Destructor
here (3)!
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.