参考的生命周期是否延长?

时间:2017-02-24 12:03:32

标签: c++ reference lifetime

我想将引用传递给函数。这段代码不起作用,正如我所期望的那样:

struct A {
};

void foo(A& a) {
    // do something with a
}

int main(int, char**) {
    foo(A());
}

我收到编译错误

  

A&类型的右值

中初始化A类型的非const引用

但是,当我将方法A& ref()添加到A时,如下所示并在传递之前调用它,似乎我可以使用a。调试时,A对象在调用foo()后被销毁:

struct A {
    A& ref() {
        return *this;
    }
};

void foo(A& a) {
    // do something with a
}

int main(int, char**) {
    foo(A().ref());
}

这个有效代码是否符合标准?调用ref()是否会神奇地延长对象的生命周期,直到foo()返回?

3 个答案:

答案 0 :(得分:21)

您的代码完全有效。

在这一行

'\buser:\s?([a-zA-Z]+)\b'

临时foo(A().ref()); 的实例一直存在到语句结尾(A)。

这就是为什么将;从[{1}}返回A&传递给ref()的原因是安全的(只要foo无法存储它)。

foo本身并没有延长任何生命周期,但它有助于返回一个左值参考。

ref()的情况会怎样?这里临时值作为右值传递。在C ++中,rvalue不绑定到非const左值引用(即使在C ++ 11中,rvalue 引用也不绑定到非const左值引用)。

从此Visual C++ blog article about rvalue references

  

... C ++并不希望你不小心修改临时数据,而是直接修改临时数据   在可修改的rvalue上调用非const成员函数是显式的,所以   它被允许......

答案 1 :(得分:7)

A()创建一个A类型的临时对象。该对象一直存在,直到创建它的完整表达式结束。原始代码中的问题是此临时代码的生命周期;它表示该函数将其参数作为非const引用,并且不允许将临时对象作为非const引用传递。最简单的变化是foo通过const引用接受它的参数,如果它适合函数的作用:

void foo(const A&);
int main() {
    foo(A());
}

答案 2 :(得分:5)

这个问题有几个问题。我会尝试解决所有问题:

首先,您不能将A类型的临时(prvalue)传递给采用A&的函数,因为非const左值引用无法绑定到rvalues。那是一种语言限制。如果您希望能够传递临时值,则需要使用类型为A&&或类型为A const&的参数 - 后者,因为临时值可以绑定到const lvalue引用。

  

这个有效代码是否符合标准?调用ref()是否会神奇地延长对象的生命周期,直到foo()返回?

您的计划根本没有终身延期。来自[class.temp]:

  

有三种情境可以在与完整表达结束时不同的时刻销毁临时对象。第一个上下文是调用默认构造函数来初始化数组的元素   没有相应的初始化程序(8.6)。第二个上下文是调用复制构造函数来复制元素的时间   复制整个数组时的数组(5.1.5,12.8)。 [...]第三个上下文是指引用绑定到临时引用。

这些背景都不适用。我们永远不会在此代码中绑定对临时的引用。 ref()*this绑定到A&,但*this不是临时的,然后将生成的引用简单地传递到foo()

考虑你的程序的这个变种:

#include <iostream>

struct A {
    A& ref() { return *this; }
    ~A() { std::cout << "~A()\n"; }
};

int main() {
    auto& foo = A().ref();
    std::cout << "----\n";
}

打印

~A()
----

说明没有终身延期。

如果不是将ref()的结果绑定到引用而是绑定成员:

#include <iostream>

struct A {
    A& ref() { return *this; }
    int x;

    ~A() { std::cout << "~A()\n"; }
};

int main() {
    auto&& foo = A().x;
    std::cout << "----\n";
}

然后我们实际上将临时绑定到引用并且第三个上下文适用 - 引用绑定到的子对象的完整对象的生命周期在引用的生命周期中保持不变。所以这段代码打印出来:

----
~A()