问题描述
我试图更好地了解如何处理左值和右值作为引用,因此我创建了这个玩具示例:
#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
的引用参数。
(重点是我的)
,每当引用绑定到临时对象或子对象时 其中,临时文件的生存期得以延长以匹配 参考的有效期,但以下情况除外:
- 在函数调用中存在绑定到引用参数的临时绑定,直到包含该函数调用的完整表达式的结尾:如果 该函数返回一个引用,该引用超出了完整的表达式, 它成为悬而未决的参考。
通常,不能通过以下方式进一步延长临时期限 “传递”:第二个引用,从引用初始化为 绑定的临时对象,不会影响其寿命。
我当时的想法是,我们可以将右值绑定到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