【问题标题】:Why Can you return a function by reference for a local variable and not for temporary variable? c++为什么可以通过引用返回局部变量而不是临时变量的函数? C++
【发布时间】:2012-03-06 15:34:23
【问题描述】:

例如这个函数 f 定义如下:

int f(int x){return x;}

如您所知,您不能为此临时 int 分配引用:

int& rf=f(2);// this will give an error

但是如果我像这样重新定义我的函数 f:

int& f(int x){return x;}
f(2);// so now f(2) is a reference of x, which has been destroyed 

所以我的问题是:编译器如何不让您创建对将在声明后销毁的临时文件的引用(第一种情况)。另一方面,它允许您创建对x 的引用 f(2),而编译器知道这个引用将在return 之后被销毁。

【问题讨论】:

  • 这是 UB(好吧,无论如何都要访问参考),任何理智的编译器都会警告你。

标签: c++ reference temporary


【解决方案1】:

返回对本地的引用是编译器很难或不可能检测到的。例如:

int & f()
{
    int x;
    extern int & g(int & x);

    // Does this return a reference to "x"?
    // The compiler has no way to tell.
    return g(x);
}

即使不调用外部函数,仍然很难分析复杂的程序流来判断返回的引用是否是本地的;与其试图定义什么算作“足够简单”来诊断,该标准不需要诊断——它只是声明它给出了未定义的行为。一个好的编译器应该给出警告,至少在简单的情况下是这样。

将临时值绑定到非常量引用是编译器可以轻松检测到的,因此标准确实需要对此进行诊断。

【讨论】:

    【解决方案2】:

    因为按照标准的规定,从函数返回对临时变量的引用是未定义的行为。

    问题其实是函数定义:

    int& f(int x)
    {
       return x;
    }
    

    【讨论】:

      【解决方案3】:

      您可以将临时右值绑定到 const 引用以延长其生命周期。

      const int& rf=f(2); 
      

      【讨论】:

        【解决方案4】:

        要拒绝您的第一个代码 sn-p,编译器会应用您不能直接将临时绑定到非常量引用的简单规则。

        为了拒绝第二个,编译器可能会应用一个规则,即通过引用返回的函数的return 语句不能是自动变量的名称(包括按值函数参数)。在我看来,这也是一个相当简单的规则。

        我不知道为什么标准没有指定这样做是不正确的。我想不出它的任何有效用途,但也许在第一个标准的时候,它会对某些实现或其他产生过度的负担。或者可能觉得它只是修复了一半,不值得费心(还有很多其他方法可以创建悬空引用,这只会阻止其中一种)。

        该标准通常不会阻止您创建绑定到临时对象的非常量引用的原因是在某些情况下可以这样做。例如:

        struct Foo {
            static void set(Foo &f) { f.val = 0; }
            int val;
            int bar() {
                set(*this);
                return val;
            }
        };
        
        std::cout << Foo().bar() << "\n";
        

        这里Foo() 是一个临时的,set(*this) 行将它绑定到一个非常量引用(但不是直接,它使用一个左值表达式*this,它有时指的是一个临时的,但不是其他的)。这里没有问题,临时的比参考的寿命长。因此,语言以某种方式阻止任何临时对象被绑定到任何非 const 引用将是不必要的限制。

        【讨论】:

          【解决方案5】:

          通过引用返回不是一个好主意,除非您确定要返回的引用仍然指向有效的东西。

          否则,会出现未定义的行为。

          由于变量是局部变量,所以可以确定引用无效

          【讨论】:

          • 由于变量是本地的,所以可以确定它是无效的。
          【解决方案6】:

          问题在于,在语义上,堆栈上的变量或堆上的变量等之间没有区别。 - 所以语言别无选择,只能允许这样做,即使它是未定义的行为。 在第一个示例中,您会遇到一个简单的编译错误,因为您尝试将引用绑定到临时变量上,而这可能会被语言所禁止。

          【讨论】:

          • 是的,我明白这一点,但为什么它适用于第二个示例?以及为什么你提到了堆,而我没有提到堆
          • @AbdessamadBond 这就是为什么你的第二个例子不起作用的答案。我提到了堆,因为返回对堆上变量的引用是可以的(即使它可能是不好的风格)。从语义上讲,堆上的变量和栈上的变量没有区别,而变量和临时值是有区别的。
          猜你喜欢
          • 1970-01-01
          • 2017-06-09
          • 1970-01-01
          • 2021-06-20
          • 2020-01-09
          • 2016-04-03
          • 2012-01-01
          • 2023-04-08
          相关资源
          最近更新 更多