3

当我阅读一些文章时,右值引用和移动语义通常是一起描述的。但是,据我了解,右值引用只是对右值的引用,与移动语义无关。甚至可以在不使用右值引用的情况下实现移动语义。所以问题是,为什么要移动 constructor/operator= 使用右值引用?只是为了更容易编写代码吗?

4

2 回答 2

8

考虑问题。我们要支持两种基本的移动操作:移动“构造”和移动“赋值”。我在那里使用引号是因为我们不一定要使用构造函数或移动赋值运算符来实现它们;我们可以用别的东西。

移动“构造”意味着通过从现有对象传输内容来创建新对象,这样删除旧对象不会释放新对象中现在使用的资源。移动“分配”意味着获取一个预先存在的对象并从现有对象转移内容,这样删除旧对象不会释放新对象中现在使用的资源。

好的,所以这些是我们想要做的操作。那么,怎么做呢?

采取行动“建设”。虽然我们不必通过构造函数调用来实现它,但我们真的很想这样做。我们不想强迫人们进行两阶段移动构建,即使它背后有一些神奇的函数调用。所以我们希望能够将运动作为构造函数来实现。好的。

这是问题 1:构造函数没有名称。因此,您只能根据参数类型和重载分辨率来区分它们。而且我们知道类型对象的移动构造函数T必须将类型对象T作为参数。而且由于它只需要一个参数,因此它看起来就像一个复制构造函数。

好的,所以现在我们需要一些方法来满足重载。我们可以引入一些标准库类型,a std::move_ref. 它会像std::reference_wrapper,但它会是一个独特的类型。因此,您可以说移动构造函数是一个带有std::move_ref<T>. 好的,好的:问题解决了。

只是没有;我们现在遇到了新问题。考虑这段代码:

std::string MakeAString() { return std::string("foo"); }

std::string data = MakeAString();

忽略省略,C++11 的表达式值类别规则规定从函数按值返回的类型是纯右值。因此,它会尽可能地被移动构造函数/赋值运算符自动使用。不需要之类的。std::move

要做到这一点,您需要这样做:

std::string MakeAString() { return std::move(std::string("foo")); }

std::string data = std::move(MakeAString());

这两个std::move调用都需要避免复制。您必须移出临时值并进入返回值,然后移出返回值并进入data(再次忽略省略)。

If you think that this is merely a minor annoyance, consider what else rvalue references buy us: perfect forwarding. Without the special reference-collapsing rules, you could not write a proper forwarding function that forwards copy and move semantics perfectly. std::move_ref would be a real C++ type; you couldn't just slap arbitrary rules like reference collapsing onto it like you can with rvalue references.

At the end of the day, you need some kind of language construct in place, not merely a library type. By making it a new kind of reference, you get to be able to define new rules for what can bind to that reference (and what cannot). And you get to define special reference-collapsing rules that make perfect forwarding possible.

于 2013-03-10T10:05:49.703 回答
1

连接是从右值移动是安全的(因为(在没有强制转换的情况下)右值指的是在其生命周期结束时的对象),所以一个采用右值引用的构造函数可以通过窃取 /从引用的对象移动。

从 C++ 语言的角度来看,这是连接的结束,但标准库通过不断地从左值复制构造和从右值std::move构造移动,并通过提供帮助函数(例如可以直接选择是移动还是复制特定对象(通过更改导致复制/移动的表达式中对象的值类别)。

移动语义可以在没有右值引用的情况下实现,但它会不那么整洁。需要解决一些问题:

  1. 如何通过非常量引用捕获右值?

  2. 如何区分复制的构造函数和移动的构造函数?

  3. 如何确保在任何可以安全优化的地方使用移动?

  4. 如何编写适用于可移动和可复制对象的通用代码?

于 2013-03-10T09:29:59.210 回答