右值引用是否允许悬空引用?
考虑以下内容。
#include <string>
using std::string;
string middle_name () {
return "Jaan";
}
int main ()
{
string&& danger = middle_name(); // ?!
return 0;
}
这不会计算任何东西,但它编译时没有错误,并演示了一些我觉得令人困惑的东西:danger
是一个悬空引用,不是吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
当然,右值引用仍然是一个引用,因此它也可以是悬空的。您只需让编译器陷入这样一种情况:他必须拖动引用,同时您只需转义引用值的范围,如下所示:
演示
输出:
如您所见,b 现在的值是错误的。怎么会?我们将对自动变量
a
的右值引用填充到全局元组中。然后我们转义了a
的范围并通过std::get> 检索它的值;它将评估为右值引用。所以新对象 b 实际上是从
a
构造的,但是编译器找不到a
因为它的作用域已经结束了。因此,std::get
的计算结果为 0(尽管它可能是 UB 并且可以计算为任何值)。请注意,如果我们不接触堆栈,右值引用实际上仍然会找到对象
a
的原始值,即使它的作用域已经结束,并且会检索正确的值(只需尝试并取消注释 < code>pollut_stack() 看看会发生什么)。pollut_stack()
函数只是向前和向后移动堆栈指针,同时通过printf()
执行一些与 io 相关的操作,将值写入堆栈。编译器根本看不透这一点,所以要注意这一点。
Of course, an rvalue reference is still a reference so it can be dangling as well. You just have to bring the compiler into a situation where he has to drag the reference along and at the same time you just escape the refered-to value's scope, like this:
Demo
Output:
As you can see, b now has the wrong value. How come? We stuffed an rvalue reference to an automatic variable
a
into a global tuple. Then we escaped the scope ofa
and retrieve its value throughstd::get<int&&
> which will evaluate to an rvalue-reference. So the new object b is actually move constructed froma
, but the compiler doesn't finda
because its scope has ended already. Thereforestd::get<int&&>
evaluates to 0 (although it is probably UB and could evaluate to anything).Note that if we don't touch the stack, the rvalue reference will actually still find the original value of object
a
even after its scope has ended and will retrieve the right value (just try it and uncommentpollute_stack()
and see what happens). Thepollute_stack()
function just moves the stack pointer forward and back while writing values to the stack by doing some io-related stuff throughprintf()
.The compiler doesn't see through this though at all so be aware of this.
与使用
const &
没什么区别:danger
取得右值的所有权。Not any more than if you had used a
const &
:danger
takes ownership of the rvalue.右值引用绑定到右值。右值可以是纯右值或x值[解释]。绑定到前者永远不会创建悬空引用,绑定到后者可能会创建悬空引用。这就是为什么选择
T&&
作为函数的返回类型通常是一个坏主意。std::move
是此规则的一个例外。rvalue references bind to rvalues. An rvalue is either a prvalue or an xvalue [explanation]. Binding to the former never creates a dangling reference, binding to the latter might. That's why it's generally a bad idea to choose
T&&
as the return type of a function.std::move
is an exception to this rule.如果您的意思是“是否可以创建悬空右值引用”,那么答案是肯定的。然而,你的例子
完全没问题。同样的规则也适用于 这个例子(赫伯·萨特的文章)也很安全。如果使用纯右值初始化引用,则临时对象的生命周期将延长到引用的生命周期。不过,您仍然可以生成悬空引用。例如,这不再安全:
因为
std::move
返回一个string&&
(这不是一个< em>纯右值)延长临时生命周期的规则不适用。这里,std::move
返回一个所谓的xvalue。 xvalue 只是一个未命名的右值引用。因此,它可以引用任何内容,并且在不查看函数的实现的情况下基本上不可能猜测返回的引用所引用的内容。If you meant "Is it possible to create dangling rvalue references" then the answer is yes. Your example, however,
is perfectly fine. The same rule applies here that makes this example (article by Herb Sutter) safe as well. If you initialize a reference with a pure rvalue, the life-time of the tempoary object gets extended to the life-time of the reference. You can still produce dangling references, though. For example, this is not safe anymore:
Because
std::move
returns astring&&
(which is not a pure rvalue) the rule that extends the temporary's life-time doesn't apply. Here,std::move
returns a so-called xvalue. An xvalue is just an unnamed rvalue reference. As such it could refer to anything and it is basically impossible to guess what a returned reference refers to without looking at the function's implementation.