将const引用返回给临时变量的行为与本地const引用不同吗?

问题描述

我试图更好地了解如何处理左值和右值作为引用,因此我创建了这个玩具示例:

#include <iostream>

struct Val
{
    Val(int num) : num(num){};
    ~Val()
    {
        std::cout << "Destructing with value " << num << std::endl;
    }

    int num;
};

const Val &test(const Val &val)
{
    return val;
}
int main()
{
    std::cout<< "Creating foo with value 5" <<std::endl;
    const Val &foo = test(Val(5));
    std::cout<< "Creating bar with value 3" <<std::endl;
    const Val &bar(3);
    std::cout<< "Finishing main function" <<std::endl;
    return 0;
}

打印输出

Creating foo with value 5
Destructing with value 5
Creating bar with value 3
Finishing main function
Destructing with value 3

基本上,我们看到此右值Val(5)绑定到函数val中的const引用参数test,并且返回了相同的值-但是,析构函数会被立即调用,因为它是临时的。但是,当我们尝试构造Val(3)并将其分配给const引用时,它仍然在整个块的范围之内。

我当时的想法是,我们可以将右值绑定到const引用,并且会延长其寿命,直到该引用超出范围,但这似乎不一定是这种情况。如果您对误解有任何见解,我将不胜感激。

解决方法

鉴于const Val &foo = test(Val(5));,临时Val(5)将在完整表达式后立即销毁,其生存期将不会扩展到引用foo的提升。它不直接绑定到foo,而是绑定到test的引用参数。

reference initialization中,

(重点是我的)

每当引用绑定到临时对象或子对象时 其中,临时文件的生存期得以延长以匹配 参考的有效期,但以下情况除外:

  • 在函数调用中存在绑定到引用参数的临时绑定,直到包含该函数调用的完整表达式的结尾:如果 该函数返回一个引用,该引用超出了完整的表达式, 它成为悬而未决的参考。

通常,不能通过以下方式进一步延长临时期限 “传递”:第二个引用,从引用初始化为 绑定的临时对象,不会影响其寿命。

,

我当时的想法是,我们可以将右值绑定到const引用,这将延长其寿命,直到该引用超出范围

是的,确实val参数确实延长了Val(5)的生存期,但是当test返回时,val本身就被销毁了,什么也没有使Val(5)保持生命,因此它也被破坏了。

通过引用返回,您实际上正在返回一个悬空引用,因此您具有未定义的行为: parameter val是对实际参数Val(5)的引用不会影响val返回后test不再可用,而您正在返回(嗯,试图返回) it val)的事实参考,而不是其参考实体。

,

当您将输出添加到test()本身

const Val &test(const Val &val)
{
    std::cout << "test with value " << val.num << '\n';
    return val;
}

您会看到,该临时对象的生存期一直到该函数结束,但不会超过该寿命

Creating foo with value 5
test with value 5
Destructing with value 5
Creating bar with value 3
Finishing main function
Destructing with value 3