2013-03-10 65 views
3

当我阅读一些文章时,右值引用和移动语义通常一起描述。然而,据我所知,右值引用只是引用右值,并且与移动语义无关。移动语义可能会被实现,甚至可能甚至不使用右值引用。所以问题是,为什么要移动构造函数/运算符=使用右值引用?只是为了更容易编写代码?为什么右值引用与移动语义相关联?

回答

8

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

移动“构造”意味着通过从现有对象中传输内容来创建新对象,从而删除旧对象不会释放现在在新对象中使用的资源。移动“赋值”意味着采用预先存在的对象并从现有对象中传输内容,以便删除旧对象不会释放现在在新对象中使用的资源。

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

移动“建筑”。虽然我们没有来执行这个带有构造函数调用,但我们真的想要。我们不想强迫人们进行两阶段移动建设,即使它是在一些神奇的功能调用之后。所以我们希望能够实现运动作为构造函数。好的。

问题1:构造函数没有名字。因此,您只能根据参数类型和重载解析来区分它们。我们知道T类型的对象的移动构造函数必须将T类型的对象作为参数。因为它只需要一个参数,所以它看起来完全像一个拷贝构造函数。好的,现在我们需要一些方法来满足超载。我们可以引入一些标准库类型,一个std::move_ref。这将像std::reference_wrapper,但它将是一个独特的类型。因此,你可以说移动构造函数是一个构造函数,它需要一个std::move_ref<T>。好,很好:问题解决了。

只有不;我们现在有新的问题。考虑下面的代码:

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

std::string data = MakeAString(); 

忽略省音,C++ 11的表情值类别规则指出这是从一个函数由返回值类型是一个prvalue。因此,它会自动被移动构造函数/赋值运算符使用。不需要std::move等。

要做到这一点的方法就是需要这样的:

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

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

那些std::move电话将需要避免复制的两个。您必须移出临时值并返回返回值,然后移出返回值并返回data(再次忽略elision)。

如果您认为这只是一个小小的烦恼,请考虑还有哪些右值引用了我们:完美转发。如果没有特殊的引用合拢规则,就无法编写正确的转发功能,它可以完美地转发副本并移动语义std::move_ref将是一个真正的C++类型;你不能像使用右值引用一样将任意规则像参考折叠一样放在它上面。

在一天结束时,您需要某种形式的语言结构,而不仅仅是一种库类型。通过使它成为一种新的参考,您可以定义新的规则,以便绑定到该参考(以及不可以)的内容。而且您可以定义特殊的引用合拢规则,使完美的转发成为可能。

1

连接的是,它是安全的从右值(因为(在不存在铸件)右值指的是在其寿命的末尾的对象),所以一个构造函数的移动右值引用可以通过从引用对象中盗取/移动而安全地实现。

从C++ - 的视图语言点,这是在连接的端部,但在标准库进一步通过不断从左值拷贝和建设从右值举动使施工此连接上膨胀,并通过提供帮助函数(例如std::move),它使得直接选择是移动还是复制特定对象(通过改变表达式中导致复制/移动的对象的值类别)。

移动语义可以在没有右值-参考的情况下实现,但它会少得多整齐。许多问题需要解决:

  1. 如何通过非const引用捕捉右值

  2. 如何区分副本的构造函数和移动的构造函数?

  3. 如何确保移动被用在任何他们将会进行安全优化的地方?

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

相关问题