【问题标题】:Is capturing a newly constructed object by const ref undefined behavior正在通过 const ref undefined 行为捕获新构造的对象
【发布时间】:2019-10-31 17:40:28
【问题描述】:

下面的(人为的例子)是好的还是未定义的行为:

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

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

【问题讨论】:

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


    【解决方案1】:

    这是安全的。 const ref 延长了临时的生命周期。范围将是 const ref 的范围。

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

    每当引用绑定到临时对象或子对象时 其中,临时的生命周期被延长以匹配 引用的生命周期,有以下例外

    • 在 return 语句中临时绑定到函数的返回值没有扩展:它在结束时立即销毁 返回表达式。这样的函数总是返回一个悬空 参考。
    • 临时绑定到构造函数初始化器列表中的引用成员仅持续到构造函数退出,而不是作为 只要对象存在。 (注意:这种初始化格式不正确,如 DR 1696)。
    • 在函数调用中与引用参数的临时绑定一直存在,直到包含该函数的完整表达式结束 调用:如果函数返回一个引用,该引用比完整的 表达式,它变成了一个悬空引用。
    • 在新表达式中使用的初始值设定项中的引用的临时绑定一直存在,直到包含的完整表达式结束 那个new-expression,不只要初始化对象。如果 初始化的对象比完整的表达式寿命长,它的引用成员 成为悬空引用。
    • 临时绑定到使用直接初始化语法(括号)初始化的聚合的引用元素中的引用 与列表初始化语法(大括号)相反,存在到最后 包含初始化程序的完整表达式。 struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference

    一般来说,临时的生命周期不能通过“传递它”来进一步延长 on":第二个引用,从 临时被绑定,不影响其生命周期。

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

    “如果c.GetSomeVariable()返回一个对本地对象的引用或一个它本身正在延长某个对象生命周期的引用,生命周期延长不会启动”

    【讨论】:

    • 你应该引用那句话的出处。
    • @LightnessRaceswithMonica 完成。我正在寻找更好的文本。
    • 最好强调这仅适用于 values。如果c.GetSomeVariable() 返回一个对本地对象的引用或一个它本身正在扩展某个对象的生命周期的引用,生命周期扩展不会起作用。
    • @KonradRudolph 谢谢!我也添加了异常。
    【解决方案2】:

    这里应该没有问题,感谢lifetime extension。新构造的对象将一直存在,直到引用超出范围。

    【讨论】:

      【解决方案3】:

      是的,这是非常安全的:绑定到 const 引用会将临时对象的生命周期延长到该引用的范围。

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

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

      cc 悬空。

      【讨论】:

        【解决方案4】:

        这是安全的。

        [class.temporary]/5:在三种情况下,临时对象在与 full-expression 结尾不同的点被销毁。 [..]

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

        【讨论】:

          【解决方案5】:

          在这种特定情况下是安全的。但是请注意,并非所有临时对象都可以通过 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)!
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-02-03
            • 1970-01-01
            • 2018-08-15
            • 2017-04-09
            • 2015-11-02
            相关资源
            最近更新 更多