右值引用:为啥不隐式移动右值?
Posted
技术标签:
【中文标题】右值引用:为啥不隐式移动右值?【英文标题】:Rvalue reference: Why aren't rvalues implicitly moved?右值引用:为什么不隐式移动右值? 【发布时间】:2013-01-16 05:00:37 【问题描述】:关于 C++ 右值引用 (http://www.artima.com/cppsource/rvalue.html) 的 Artima 文章中有一句话:这就是为什么在传递给基类时必须说 move(x) 而不仅仅是 x。这是移动语义的一个关键安全特性,旨在防止从某个命名变量意外移动两次。
我想不出这种双招可以执行的情况。你能举个例子吗?换句话说,如果T&&
的所有成员都是右值引用而不仅仅是引用,会出现什么问题?
【问题讨论】:
【参考方案1】:考虑这种情况:
void foo(std::string x)
void bar(std::string y)
void test(std::string&& str)
// to be determined
我们想用str
调用foo
,然后用str
调用bar
,两者的值相同。最好的方法是:
foo(str); // copy str to x
bar(std::move(str)); // move str to y; we move it because we're done with it
这样做是错误的:
foo(std::move(str)); // move str to x
bar(std::move(str)); // move str to y...er, except now it's empty
因为在第一次移动之后,str
的值是未指定的。
所以在右值引用的设计中,这种隐含的移动是不存在的。如果是这样,我们上面最好的方法就行不通了,因为第一次提到 str
会是 std::move(str)
。
【讨论】:
【参考方案2】:我的看法是,如果我们有一些右值引用 x:
T&& x = ...;
我们使用 x 作为参数调用了一些函数:
f(x)
我们需要以某种方式告诉 f 它是否会损坏 x(或“获得 x 的所有权”,或“是最后一个使用 x 的客户端”)。
一种设计方法是限定每个调用:
f(yours_now(x)) // ok to damage
f(still_mine(x)) // dont damage
并使不合格的调用非法。
另一种方式是将一种方式设为默认方式:
要么:
f(yours_now(x)) // ok to damage
f(x) // dont damage
或
f(x) // ok to damage
f(still_mine(x)) // dont damage
因此,如果我们同意限定每次使用都过于庞大,我们应该默认使用一种方式,哪种方式最好?好吧,让我们看看在这两种情况下意外选择默认值的代价:
在第一种情况下,损坏是可以的,但我们不小心说它不是。在这种情况下,我们会因为制作了不必要的副本而失去性能,但除此之外没什么大不了的。
在第二种情况下,损坏对象是不行的,但我们不小心说它是。这可能会导致程序中难以检测到逻辑错误,因为 x 现在在 f 返回时处于损坏状态,但作者预计不会如此。
所以选择第一种情况是因为它“更安全”。
【讨论】:
以上是关于右值引用:为啥不隐式移动右值?的主要内容,如果未能解决你的问题,请参考以下文章