如何理解std::move和std::forward的深层机制?

摘要:本文翻译自《effective modern C++》,由于水平有限,故无法保证翻译完全正确,欢迎指出错误。谢谢! 博客已经迁移到 "这里啦" 根据st
本文翻译自《effective modern C++》,由于水平有限,故无法保证翻译完全正确,欢迎指出错误。谢谢! 博客已经迁移到这里啦 根据std::move和std::forward不能做什么来熟悉它们是一个好办法。std::move没有move任何东西,std::forward没有转发任何东西。在运行期,它们没有做任何事情。它们没有产生需要执行的代码,一byte都没有。 std::move和std::forward只不过就是执行cast的两个函数(实际上是函数模板)。std::move无条件地把它的参数转换成一个右值,而std::forward只在特定条件满足的情况下执行这个转换。就是这样了,我的解释又引申出一系列的新问题,但是,基本上来说,上面说的就是全部内容了。 为了让内容更加形象,这里给出C++11中std::move实现的一个例子。它没有完全遵循标准的细节,但是很接近了。 template<typename T> //在命名空间std中 typename remove_reference<T>::type&& move(T&& param) { using ReturnType = //别名声明 typename remove_reference<T>::type&&; //看Item 9 return static_cast<ReturnType>(param); } 我已经帮你把代码的两个部分高亮(move和static_cast)显示了。一个是函数的名字,因为返回值类型挺复杂的,我不想让你在这复杂的地方浪费时间。另一个地方是包括了这个函数的本质(cast)。就像你看到的那样,std::move需要一个对象的引用(准确地说是一个universal引用,看Item 24),并且返回同一个对象的引用。 函数返回值类型的“&&”部分暗示了std::move返回一个右值引用,但是,就像Item 28解释的那样,如果类型T恰好是左值引用,T&&将成为一个左值引用。为了防止这样的事情发生,type trait(看Item 9)std::remove_reference被用在T上了,因此能保证把“&&”加在不是引用的类型上。这样能保证让std::move确切地返回一个右值引用,并且这是很重要的,因为由函数返回的右值引用是一个右值。因此,std::move所做的所有事情就是转换它的参数为一个右值。 说句题外话,在C++14中std::move能被实现得更简便一些。多亏了函数返回值类型推导(看Item 3)以及标准库的别名模板std::remove_reference_t(看Item 9),std::move能被写成这样: template<typename T> decltype(auto) move(T&& param) { using ReturnType = remove_reference_t<T>&&; return static_cast<ReturnType>(param); } 看上去更简单了,不是吗? 因为std::move值只转换它的参数为右值,这里有一些更好的名字,比如说rvalue_cast。尽管如此,我们仍然使用std::move作为它的名字,所以记住std::move做了什么和没做什么很重要。它做的是转换,没有做move。 当然了,右值是move的候选人,所以把std::move应用在对象上能告诉编译器,这个对象是有资格被move的。这也就是为什么std::move有这样的名字:能让指定的对象更容易被move。 事实上,右值是move的唯一候选人。假设你写了一个代表注释的类。这个类的构造函数有一个std::string的参数,并且它拷贝参数到一个数据成员中。根据Item 41中的信息,你声明一个传值的参数: class Annotation { public: explicit Annotation(std::string text); // 要被拷贝的参数 // 根据Item 41,声明为传值的 ... }; 但是Annotation的构造函数只需要读取text的值。它不需要修改它。
阅读全文