【问题标题】:C++: constant reference to temporaryC++:对临时的常量引用
【发布时间】:2010-12-23 22:23:26
【问题描述】:

关于 SO 上的常量引用的生命周期有几个问题,但我还是不明白。

这段代码有效吗?

struct S
{
    const int &ref;
    S( const int &x ) : ref(x) { }
};

int main( )
{
    S s( 0 );
    // ...
    use( s.ref );
    // ...
    return 0;
}

直觉上我会说不,因为0 应该在计算表达式 (S s(0);) 后过期。

但是 GCC 和 CLANG 编译它都很好,没有警告,而且 valgrind 没有检测到任何运行时错误。

关于引用我缺少什么?

【问题讨论】:

  • 当您将引用传递给接受引用的对象构造函数时,您可能会在所构造对象的生命周期内放弃引用。然后有责任确保引用的对象比新构造的对象的寿命更长。

标签: c++ reference constants temporary lifetime


【解决方案1】:

根据 12.2/4 对我来说似乎无效:

有两种情况 临时工被摧毁 与结束点不同的点 充分表达。第一个上下文是 当表达式显示为 声明器的初始化器定义 一个对象在这种情况下, 临时保存结果的 表达式将持续到 对象初始化完成

临时对象只能在 s 完全构建之前存在,直到 use 被调用为止。

【讨论】:

    【解决方案2】:

    正如其他人指出的那样,C++ 标准仅强制编译器在调用构造函数期间将0 临时保留。实际上,gcc 在 main 函数的持续时间内保持临时状态,从而导致程序按预期运行。因此,不会出现警告或运行时错误。

    但这只是偶然的。不要依赖这种行为。

    【讨论】:

      【解决方案3】:

      这里要注意的不是const,而是引用。 const 只是静态分析的工具。您需要小心引用,因为它们会咬人。

      int& f()
      {
          int i = 2;
          return i;
      }
      

      有时编译器足够聪明,可以警告您运行时会出现的问题,但有时不会。无论哪种方式,编译器都没有警告你。

      【讨论】:

      • 编译器会警告返回临时对象(至少是 GCC 和 CLANG)。无论如何,Valgrind 不应该报告运行时错误吗?当然这取决于很多事情,我应该看看生成的代码,反正我觉得很难去想……
      • @peoro 我发布的代码导致了未定义的行为。 UB 意味着事情可能会也可能不会爆炸,现在或以后,以您期望的方式或您不期望的方式。因此,valgrind 可能会或可能不会在您的特定运行中发现问题。
      【解决方案4】:

      这是对代码的另一个调整,甚至 valgrind 都会抱怨:

      #include <iostream>
      
      struct S
      {
          const int &ref;
          S( const int &x ) : ref(x) { }
      };
      
      S* foo()
      {
          return new S(0);
      }
      
      int main( )
      {
          S* s = foo();
          std::cout << s->ref << std::endl;
          return 0;
      }
      

      这通常将临时文件放在foo 函数的堆栈帧中,因此当该函数返回时它会被销毁。这类似于返回局部变量的地址。

      其他答案已经指出了为什么允许编译器这样做,我的代码只是一个说明。

      【讨论】:

        【解决方案5】:

        0 不是临时的,而是文字。尝试对您的程序进行这个小改动:

        struct S 
        {
            const int &ref;
            S( const int &x ) : ref(x) { }
        };
        
        int f()
        {
            return 0;
        }
        
        int main( )
        {
            S s( f() );
            // ...
            use( s.ref );
            // ...
            return 0;
        }
        

        我认为引用临时的规则只适用于局部变量,而不是成员。

        【讨论】:

        • 0 字面意思是真的,但我认为在这种情况下(常量左值引用参数)会创建一个临时变量。您的代码不应与原始代码有任何不同。
        • 在这种情况下,文字和临时有什么区别?无论如何,它也可以将f() 传递给S 的构造函数。
        • @Ben Voigt,我知道它的行为不应该有任何不同,但我认为它可能会触发编译器的一些警告。现在我再想一想,我明白为什么它可能不会。
        • @peoro:区别在于文字是右值,而临时值是左值。
        猜你喜欢
        • 2011-05-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多