9
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 输出。所以没有延长寿命。

为什么?我的意思是有人可以逐步解释发生了什么吗?

我的推理链中的错误在哪里?

富():

  1. A(32) - 演员

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

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

(但实际上析构函数是在复制构造函数之前调用的)

foo_2():

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

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

(但实际上析构函数是在复制构造函数之前调用的)

4

2 回答 2

12

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

12.2 临时对象

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

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

你似乎认为你的临时工应该以某种方式活得比这更长。显然,您正在尝试应用这条规则,即临时文件应该一直存在到完整表达式结束。但该规则不适用于在函数内部创建的临时对象。此类临时人员的生命受到他们自己的专用规则的约束。

如果有人试图使用返回的引用,您foo和您都会产生未定义的行为。foo_2

于 2012-07-31T19:38:35.173 回答
4

您在误解“直到函数退出”。如果您真的想使用 const 引用来延长对象的寿命foo,请使用

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

如果您希望以您期望的方式扩展事物,您必须从foo 按值返回,然后使用 const 引用来引用返回值。

正如@AndreyT 所说,该对象在具有const &. 您希望您的对象在 . 之后仍然存在 ,因此foo应该在. 第一次提到的应该是 in ,因为那是应该让对象保持活动状态的函数。const &&foofooconst &main

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

于 2012-08-01T01:13:45.557 回答