下文先從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>(); }