【C++ Primer | 16】std::move和std::forward、完美转发
右值引用應(yīng)該是C++11引入的一個(gè)非常重要的技術(shù),因?yàn)樗且苿?dòng)語(yǔ)義(Move semantics)與完美轉(zhuǎn)發(fā)(Perfect forwarding)的基石:
- 移動(dòng)語(yǔ)義:將內(nèi)存的所有權(quán)從一個(gè)對(duì)象轉(zhuǎn)移到另外一個(gè)對(duì)象,高效的移動(dòng)用來(lái)替換效率低下的復(fù)制,對(duì)象的移動(dòng)語(yǔ)義需要實(shí)現(xiàn)移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值運(yùn)算符。
- 完美轉(zhuǎn)發(fā):定義一個(gè)函數(shù)模板,該函數(shù)模板可以接收任意類(lèi)型參數(shù),然后將參數(shù)轉(zhuǎn)發(fā)給其它目標(biāo)函數(shù),且保證目標(biāo)函數(shù)接受
?
一、 引入的新規(guī)則
1. 規(guī)則1(引用折疊規(guī)則):如果間接的創(chuàng)建一個(gè)引用的引用,則這些引用就會(huì)“折疊”。在所有情況下(除了一個(gè)例外),引用折疊成一個(gè)普通的左值引用類(lèi)型。一種特殊情況下,引用會(huì)折疊成右值引用,即右值引用的右值引用, T&& &&。即
- X& &、X& &&、X&& &都折疊成X&
- X&& &&折疊為X&&
2. 規(guī)則2(右值引用的特殊類(lèi)型推斷規(guī)則):當(dāng)將一個(gè)左值傳遞給一個(gè)參數(shù)是右值引用的函數(shù),且此右值引用指向模板類(lèi)型參數(shù)(T&&)時(shí),編譯器推斷模板參數(shù)類(lèi)型為實(shí)參的左值引用,如
template<typename T> void f(T&&);int main() {int i = 42;f(i) }上述的模板參數(shù)類(lèi)型T將推斷為int&類(lèi)型,而非int。
- 若將規(guī)則1和規(guī)則2結(jié)合起來(lái),則意味著可以傳遞一個(gè)左值int i給f,編譯器將推斷出T的類(lèi)型為int&。再根據(jù)引用折疊規(guī)則 void f(int& &&)將推斷為void f(int&)。
- 從上述兩個(gè)規(guī)則可以得出結(jié)論:如果一個(gè)函數(shù)形參是一個(gè)指向模板類(lèi)型的右值引用,則該參數(shù)可以被綁定到一個(gè)左值上。
- 規(guī)則3:雖然不能隱式的將一個(gè)左值轉(zhuǎn)換為右值引用,但是可以通過(guò)static_cast顯示地將一個(gè)左值轉(zhuǎn)換為一個(gè)右值。【C++11中為static_cast新增的轉(zhuǎn)換功能】。
?
std::move()解析
class Foo { public:std::string member;Foo(const std::string& m): member(m) {} // Copy member. Foo(std::string&& m): member(std::move(m)) {} // Move member. };上述Foo(std::string&& member)中的member是rvalue reference,但是member卻是一個(gè)左值lvalue,因此在初始化列表中需要使用std::move將其轉(zhuǎn)換成rvalue。?
標(biāo)準(zhǔn)庫(kù)中move的定義如下:
template<typename T> typename remove_reference<T>::type&& move(T&& t) {return static_cast<typename remove_reference<T>::type &&>(t); }move函數(shù)的參數(shù)T&&是一個(gè)指向模板類(lèi)型參數(shù)的右值引用【規(guī)則2】,通過(guò)引用折疊,此參數(shù)可以和任何類(lèi)型的實(shí)參匹配,因此move既可以傳遞一個(gè)左值,也可以傳遞一個(gè)右值;
std::move(string("hello"))調(diào)用解析:
- 首先,根據(jù)模板推斷規(guī)則,確地T的類(lèi)型為string;
- typename remove_reference<T>::type && 的結(jié)果為 string &&;
- move函數(shù)的參數(shù)類(lèi)型為string&&;
- static_cast<string &&>(t),t已經(jīng)是string&&,于是類(lèi)型轉(zhuǎn)換什么都不做,返回string &&;
string s1("hello"); std::move(s1); 調(diào)用解析:
- 首先,根據(jù)模板推斷規(guī)則,確定T的類(lèi)型為string&;
- typename remove_reference<T>::type && 的結(jié)果為 string&
- move函數(shù)的參數(shù)類(lèi)型為string& &&,引用折疊之后為string&;
- static_cast<string &&>(t),t是string&,經(jīng)過(guò)static_cast之后轉(zhuǎn)換為string&&, 返回string &&;
從move的定義可以看出,move自身除了做一些參數(shù)的推斷之外,返回右值引用本質(zhì)上還是靠static_cast<T&&>完成的。
因此下面兩個(gè)調(diào)用是等價(jià)的,std::move就是個(gè)語(yǔ)法糖。
void func(int&& a) {cout << a << endl; }int a = 6; func(std::move(a));int b = 10; func(static_cast<int&&>(b));std::move執(zhí)行到右值的無(wú)條件轉(zhuǎn)換。就其本身而言,它沒(méi)有move任何東西。
?
三、std::forward()解析?
?
class Foo { public:std::string member;template<typename T>Foo(T&& member): member{std::forward<T>(member)} {} };傳遞一個(gè)lvalue或者傳遞一個(gè)const lvaue
- 傳遞一個(gè)lvalue,模板推導(dǎo)之后 T = std::string&
- 傳遞一個(gè)const lvaue, 模板推導(dǎo)之后T = const std::string&
- T& &&將折疊為T(mén)&,即std::string& && 折疊為 std::string&
- 最終函數(shù)為: Foo(string& member): member{std::forward<string&>(member)} {}
- std::forward<string&>(member)將返回一個(gè)左值,最終調(diào)用拷貝構(gòu)造函數(shù)
傳遞一個(gè)rvalue
- 傳遞一個(gè)rvalue,模板推導(dǎo)之后 T = std::string
- 最終函數(shù)為: Foo(string&& member): member{std::forward<string>(member)} {}
- std::forward<string>(member) 將返回一個(gè)右值,最終調(diào)用移動(dòng)構(gòu)造函數(shù);
總結(jié):
1. std::move和std::forward本質(zhì)都是轉(zhuǎn)換。std::move執(zhí)行到右值的無(wú)條件轉(zhuǎn)換。std::forward只有在它的參數(shù)綁定到一個(gè)右值上的時(shí)候,才轉(zhuǎn)換它的參數(shù)到一個(gè)右值。
2. std::move沒(méi)有move任何東西,std::forward沒(méi)有轉(zhuǎn)發(fā)任何東西。在運(yùn)行期,它們沒(méi)有做任何事情。它們沒(méi)有產(chǎn)生需要執(zhí)行的代碼,一byte都沒(méi)有。
測(cè)試代碼:
#include <iostream> using namespace std;template<typename T> void PrintT(T& t) {cout << "lvaue" << endl; }template<typename T> void PrintT(T&& t) {cout << "rvalue" << endl; }template<typename T> void TestForward(T&& v) {PrintT(v);PrintT(std::forward<T>(v));PrintT(std::move(v));cout << "--------" << endl; }int main() {TestForward(1);int x = 1;TestForward(x);TestForward(std::forward<int>(x)); }輸出結(jié)果:
?
[ 1 ].?C++11改進(jìn)我們的程序之move和完美轉(zhuǎn)發(fā)
[ 2 ].?C++11 std::move和std::forward
3.?C++完美轉(zhuǎn)發(fā)
總結(jié)
以上是生活随笔為你收集整理的【C++ Primer | 16】std::move和std::forward、完美转发的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C++ 内存管理机制
- 下一篇: 【C++ Primer | 16】容器适