10

在关于 C++ 右值参考 ( http://www.artima.com/cppsource/rvalue.html ) 的 Artima 文章中,有一句话:这就是为什么在传递给基类时有必要说 move(x) 而不仅仅是 x . 这是移动语义的一个关键安全特性,旨在防止从某个命名变量意外移动两次。

我想不出这样的双招可以执行的情况。你能举个例子吗?换句话说,如果所有成员T&&都是右值引用而不仅仅是引用,会出现什么问题?

4

2 回答 2

18

考虑这种情况:

void foo(std::string x) {}
void bar(std::string y) {}

void test(std::string&& str)
{
    // to be determined
}

我们想调用foowith str,然后调用barwith str,两者都具有相同的值。最好的方法是:

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是未指定的。

所以在右值引用的设计中,这种隐含的举动是不存在的。如果是这样,我们上面最好的方法就行不通了,因为第一次提到的strstd::move(str)

于 2013-01-16T05:15:08.087 回答
4

我的看法是,如果我们有一些右值引用 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 返回时处于损坏状态,但作者预计不会如此。

所以第一种情况是因为它“更安全”而被选择的。

于 2013-01-16T08:24:00.533 回答