下文先从C++11引入的几个规则,如引用折叠、右值引用的特殊类型推断规则、static_cast的扩展功能提及,而后经过例子解析std::move和std::forward的推导解析过程,说明std::move和std::forward本质就是一个转换函数,std::move执行到右值的无条件转换,std::forward执行到右值的有条件转换,在参数都是右值时,两者就是等价的。其实std::move和std::forward就是在C++11基本规则之上封装的语法糖。ios
规则1(引用折叠规则):若是间接的建立一个引用的引用,则这些引用就会“折叠”。在全部状况下(除了一个例外),引用折叠成一个普通的左值引用类型。一种特殊状况下,引用会折叠成右值引用,即右值引用的右值引用, T&& &&。即ide
规则2(右值引用的特殊类型推断规则):当将一个左值传递给一个参数是右值引用的函数,且此右值引用指向模板类型参数(T&&)时,编译器推断模板参数类型为实参的左值引用,如函数
template<typename T> void f(T&&); int i = 42; f(i)
上述的模板参数类型T将推断为int&类型,而非int。spa
若将规则1和规则2结合起来,则意味着能够传递一个左值
int i
给f,编译器将推断出T的类型为int&。再根据引用折叠规则 void f(int& &&)将推断为void f(int&),所以,f将被实例化为: void f<int&>(int&)。code
从上述两个规则能够得出结论:若是一个函数形参是一个指向模板类型的右值引用,则该参数能够被绑定到一个左值上,即相似下面的定义:orm
template<typename T> void f(T&&);
规则3:虽然不能隐式的将一个左值转换为右值引用,可是能够经过static_cast显示地将一个左值转换为一个右值。【C++11中为static_cast新增的转换功能】。blog
class Foo { public: std::string member; // Copy member. Foo(const std::string& m): member(m) {} // Move member. Foo(std::string&& m): member(std::move(m)) {} };
上述Foo(std::string&& member)
中的member是rvalue reference,可是member倒是一个左值lvalue,所以在初始化列表中须要使用std::move将其转换成rvalue。rem
标准库中move的定义以下:get
template<typename T> typename remove_reference<T>::type && move(T&& t) { return static_cast<typename remove_reference<T>::type &&>(t); }
从move的定义能够看出,move自身除了作一些参数的推断以外,返回右值引用本质上仍是靠static_cast<T&&>完成的。编译器
所以下面两个调用是等价的,std::move就是个语法糖。
void func(int&& a) { cout << a << endl; } int a = 6; func(std::move(a)); int b = 10; func(static_cast<int&&>(b));
std::move执行到右值的无条件转换。就其自己而言,它没有move任何东西。
完美转发实现了参数在传递过程当中保持其值属性的功能,即如果左值,则传递以后仍然是左值,如果右值,则传递以后仍然是右值。
C++11 lets us perform perfect forwarding, which means that we can forward the parameters passed to a function template to another function call inside it without losing their own qualifiers (const-ref, ref, value, rvalue, etc.).
std::forward只有在它的参数绑定到一个右值上的时候,它才转换它的参数到一个右值。
class Foo { public: std::string member; template<typename T> Foo(T&& member): member{std::forward<T>(member)} {} };
传递一个lvalue或者传递一个const lvaue
T = std::string&
T = const std::string&
T& &&
将折叠为T&,即std::string& && 折叠为 std::string&
Foo(string& member): member{std::forward<string&>(member)} {}
传递一个rvalue
T = std::string
Foo(string&& member): member{std::forward<string>(member)} {}
std::move和std::forward本质都是转换。std::move执行到右值的无条件转换。std::forward只有在它的参数绑定到一个右值上的时候,才转换它的参数到一个右值。
std::move没有move任何东西,std::forward没有转发任何东西。在运行期,它们没有作任何事情。它们没有产生须要执行的代码,一byte都没有。
#include <iostream> #include <type_traits> #include <typeinfo> #include <memory> using namespace std; struct A { A(int&& n) { cout << "rvalue overload, n=" << n << endl; } A(int& n) { cout << "lvalue overload, n=" << n << endl; } }; class B { public: template<class T1, class T2, class T3> B(T1 && t1, T2 && t2, T3 && t3) : a1_(std::forward<T1>(t1)), a2_(std::forward<T2>(t2)), a3_(std::forward<T3>(t3)) { } private: A a1_, a2_, a3_; }; template <class T, class U> std::unique_ptr<T> make_unique1(U&& u) { //return std::unique_ptr<T>(new T(std::forward<U>(u))); return std::unique_ptr<T>(new T(std::move(u))); } template <class T, class... U> std::unique_ptr<T> make_unique(U&&... u) { //return std::unique_ptr<T>(new T(std::forward<U>(u)...)); return std::unique_ptr<T>(new T(std::move(u)...)); } int main() { auto p1 = make_unique1<A>(2); int i = 10; auto p2 = make_unique1<A>(i); int j = 100; auto p3 = make_unique<B>(i, 2, j); return 0; }
#include <iostream> // std::cout #include <type_traits> // std::is_same template<class T1, class T2> void print_is_same() { std::cout << std::is_same<T1, T2>() << '\n'; } int main() { std::cout << std::boolalpha; print_is_same<int, int>(); print_is_same<int, int &>(); print_is_same<int, int &&>(); print_is_same<int, std::remove_reference<int>::type>(); print_is_same<int, std::remove_reference<int &>::type>(); print_is_same<int, std::remove_reference<int &&>::type>(); }