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

时间:2019-10-31 17:40:28

标签: c++ c++11 const undefined-behavior

以下(拟做的示例)还可以,还是不确定的行为?

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

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

5 个答案:

答案 0 :(得分:12)

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

  

临时对象的lifetime可以通过绑定到   const左值引用或右值引用(自C ++ 11起),请参见   reference initialization了解详情。

     

每当引用绑定到临时对象或子对象时   其中,临时文件的生存期被延长以匹配   参考的有效期,具有以下例外情况

     
      
  • 在返回语句中与函数的返回值绑定的临时绑定不会扩展:它在   返回表达式。此类函数总是返回悬空状态   参考。
  •   
  • 绑定到构造函数初始化器列表中的引用成员的临时绑定仅持续到构造函数退出之前,而不是   只要对象存在。 (注意:此类初始化的格式不正确,例如   (DR 1696)。
  •   
  • 与函数调用中的引用参数的临时绑定一直存在,直到包含该函数的完整表达式的末尾为止   调用:如果函数返回引用,则该引用的寿命超过了整个   表达式,它成为悬挂的参考。
  •   
  • 与new表达式中使用的初始化程序中的引用的临时绑定存在,直到包含以下内容的完整表达式的结尾   该new表达式,不如初始化对象长。如果   初始化对象的寿命超过了完整表达式及其引用成员   成为悬挂的参考。
  •   
  • 临时绑定到使用直接初始化语法(括号)初始化的聚合的引用元素中的引用   与列表初始化语法(大括号)相对,直到结束   包含初始化程序的完整表达式。 struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference
  •   
     

通常,不能通过“通过临时”来进一步延长临时的寿命   on”:第二个参考,从该参考初始化   临时文件已绑定,不会影响其寿命。

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

  

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

答案 1 :(得分:4)

感谢lifetime extension,在这里应该没有问题。新建对象将保留下来,直到引用超出范围为止。

答案 2 :(得分:3)

是的,这是非常安全的:绑定到const引用可以将临时生存期延长到该引用的范围。

请注意,该行为不是可传递的。例如,使用

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

cc悬挂。

答案 3 :(得分:2)

这很安全。

  

[class.temporary]/5:在三种情况下,临时变量在与 full-expression 末尾不同的位置被销毁。 [..]

     

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

答案 4 :(得分: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)!