将const引用返回给本地对象时到底发生了什么?

时间:2012-07-31 19:33:38

标签: c++ const-reference

struct A {
    A(int) : i(new int(783)) {
        std::cout << "a ctor" << std::endl;
    }

    A(const A& other) : i(new int(*(other.i))) {
        std::cout << "a copy ctor" << std::endl;
    }

    ~A() {
        std::cout << "a dtor" << std::endl;
        delete i;
    }

    void get() {
        std::cout << *i << std::endl;
    }

private:
    int* i;
};

const A& foo() {
    return A(32);
}

const A& foo_2() {
    return 6;
}

int main()
{
    A a = foo();
    a.get();
}

我知道,返回对本地值的引用是不好的。但是,另一方面,const引用应该延长临时对象的生命周期。

此代码生成UB输出。所以没有生命延伸。

为什么呢?我的意思是,有人可以解释一下子发生什么事吗?

我的推理链中哪里出错?

FOO():

  1. A(32) - ctor

  2. 返回A(32) - 创建一个对本地对象的const引用并返回

  3. a a = foo(); - a由foo()返回值初始化,返回值超出范围(表达式外)并被销毁,但a已初始化;

  4. (但实际上析构函数在复制构造函数之前被调用)

    FOO_2():

    1. return 6 - 隐式创建A类型的临时对象,创建对该对象的const引用(延长其生命周期)并返回

    2. a a = foo(); - a由foo()返回值初始化,返回值超出范围(表达式外)并被销毁,但a已初始化;

    3. (但实际上析构函数在复制构造函数之前被调用)

2 个答案:

答案 0 :(得分:12)

语言规范中明确规定了每个特定上下文的临时生命周期扩展规则。而且它说

  

12.2临时对象

     

5第二个上下文是指引用绑定到临时的。 [...]临时绑定到函数返回语句中的返回值   (6.6.3)持续到函数退出。 [...]

您的临时对象在函数退出时被销毁。这发生在收件人对象的初始化开始之前。

你似乎认为你的临时活动应该比这更长寿。显然,您正在尝试应用规则,该规则表明临时应该存在直到完整表达式结束。但该规则不适用于在函数内部创建的临时数。这些临时工的生命周期由他们自己的专门规则决定。

如果有人试图使用返回的引用,则foofoo_2都会产生未定义的行为。

答案 1 :(得分:3)

你错误地“直到功能退出”。如果你真的想使用const引用来延长对象的寿命超过foo,请使用

A foo() {
    return A(32);
}
int main() {
    const A& a = foo();
}

如果您希望以您期望的方式扩展内容,则必须按值{/ 1>从foo 返回,然后使用const引用来引用返回值。

正如@AndreyT所说,该对象在具有const &的函数中被销毁。您希望对象在foo之后存活,因此您拥有const & (或&foo中的任意位置或foo的返回类型。第一次提到const &应该在main中,因为这是应该保持对象存活的函数。

您可能认为这个按值返回的代码很慢,因为在返回中似乎有A副本,但这是不正确的。在大多数情况下,编译器只能在其最终位置(即在调用函数的堆栈上)构造一次A,然后设置相关的引用。

相关问题