C++ 右值引用 / 移動語義 / universal reference / 完美轉(zhuǎn)發(fā): 4行代碼的故事

1 右值引用

A 
GetA( ) { return A(); }

GetA() return 右值 temp_obj 

pass by value -> return 右值 temp_obj -> copy

`第 1 / 2 行 code`

A a = GetA(); / A&& a = GetA();

右值 temp_obj 賦值 / 被綁定 -> copy / no copy

右值引用 對象:右值 temp_obj lifetime 延續(xù)

到 和 右值引用對象 a 一樣長
1.jpg
2.jpg

2 move 語義

`第 3 行 code`

A(A&& rhs) : ptr(rhs.ptr) ) { rhs.ptr = nullptr; }

1. 解決的問題

class 含 ptr mem

1) shallow copy ctor: only copy ptr

=>

同一 dynamic memory 被 delete > 1 次

, 第 2次 delete 開始,

指針懸掛

2) deep copy: also copy memory

=>

不必要的 heap memory copy

可能 1 份 heap memory 就能實現(xiàn)

3) move

solve 上面 2 個 Prob
3.jpg
4.jpg

2. 3 種 引用

左 / 右 / 常量左 值引用: 只能 / 只能 / 可 綁定 左值 / 右值 / ( const / non-const ) 左 or 右值 obj, 用 T& / T&& / const T&

綁定右值, 可減少一次 copy

lambda expr 是 rvalue

void AcceptVal(A a) { }
void AcceptRef(const A& a) { }

AcceptVal( GetA() ); // 應調(diào) 2 次 copy ctor
AcceptRef( GetA() ); // 應調(diào) 1 次 copy ctor
`實際上, 兩者 1次 copy ctor 都沒調(diào)`
 
-> 

compiler 返回值優(yōu)化

2) 不是所有的 return value 都能被 compiler 優(yōu)化, 但可被 move 語義 優(yōu)化

3. 匹配順序

`右值 temp_obj copy / assignment / move 都有 時,` 

`匹配順序:`

(1) 右值引用 > 常量左值引用

`copy/move ctor/assignment + move 都有 時,`

`匹配順序:`

(2) move 語義 > copy 語義

`reason: move/copy 語義 的 function 
只 / 可接受 右 / 左 or 右 值參數(shù)`
// shallow / deep copy & move
class A
{
private:
    int* ptr;  // ptr mem
public:
    A() : ptr( new int(0) ) {}

    //(1) shallow copy ctor: only copy ptr
    A(const A& rhs) : ptr(rhs.ptr) { } 

    //(2) deep copy ctor   : also copy memory 
    A(const A& rhs) : ptr(new int(*rhs.ptr) ) { }  

    //(3) move: copy ptr + src ptr set NULL
    A(A&& rhs) : ptr(rhs.ptr) ) { rhs.ptr = nullptr; } 

    ~A(){ delete ptr; }
};

A 
GetA(){ return A(); }

int main()
{
    A a = GetA();
}
// string 的 move 語義
class MyString
{
private:
   char* pc;
public:
    // copy assignment
    MyString& 
    operator=(const MyString& rhs)
    {
        if (this == &rhs) 
            return *this;

        delete[] pc;
        
        pc = new char[ strlen(rhs.pc)  +  1];
        strcpy(pc, rhs.pc);
        
        return *this;
    }

    // move assignment
    MyString& 
    operator=(MyString&& rhs) noexcept
    {
        if (this == &rhs) 
          return *this;

        delete[] pc;
        
        pc     = rhs.pc;    
        rhs.pc = nullptr; 
        
        return *this;
    }
    
    ~MyString() { delete[] pc; }
};

3 universal ( 通用 / undefined ) references => 左 or 右 值引用

(1) T&& 與 template 結(jié)合, 且 自動類型推導 ( func template ) 時, T&& 類型不確定 需要推導 => T&& 才是 universal references

(2) universal references: 傳遞 左/右 值, 就是 左/右值引用

完美轉(zhuǎn)發(fā) 正利用該特性

template<typename T>
void f( T&& para){}

//(1) => T = int => + && -> int&&
f(10);  // arg 是右值 => T推導成 int => para 是 int&&, 是 右值引用

int x = 10; 
//(2) => T = int& => + && -> int&
f(x);   //arg 是左值 => T 推導成 int& => para 是 int&&& 折疊成 int&, 是 左值引用
//1
template<typename T>
void f( T&& para); // 調(diào) f 時, T 需推導 => para: universal references 

//2  
template<typename T>
class A{
  A(A&& a); //(1)A type 確定 => para ( a ): 右值引用
};

//3 vector<T> 的 type 必須確定 => T type 必須確定 
// => para 無需推導 => para: 右值引用
template<typename T>
void f(std::vector<T>&& para); 

4 完美轉(zhuǎn)發(fā)

`第 4 行 代碼`
// 完美轉(zhuǎn)發(fā) std::forward
template <typename T>
void f(T&& val)   // val : universal references 
{
    foo( std::forward<T>(val) );
}

完美轉(zhuǎn)發(fā) ( perfect forward ): func template 的 para 作 arg 傳給 另一 forward func 時, 左/右值特性 不變

函數(shù)調(diào)用 之 形實結(jié)合: 按 arg value 的 左右值屬性 ( lvalue / rvalue ) 匹配 相應 para type

背景

(1) 無 universal reference 時 => value 經(jīng) 2 次 轉(zhuǎn)發(fā) => rvalue 變 lvalue

void f2(int&& para2) {}
void f2(const int& para2) {} //para2:const lvalue ref -> arg = lvalue / rvalue

// arg (val): lvalue -> 應調(diào) f1(int& para1) => T = int -> + & = int&
//      -> para1 作 arg: lvalue -> 調(diào) f2(int& para2)
template <typename T>
void f1(T& para1)  { f2(para1); }


// arg: (0) rvalue -> 應調(diào) f1(const int& para1) => T = int -> + & = const int&
//      -> para1 作 arg: lvalue -> 調(diào) f2(const int& para2)
template <typename T>
void f1(const T& para1) { f2(para1); }

int val = 0;
f1(val); // (1)

f1(0); // (2)

(2) 僅 universal references => 2 次轉(zhuǎn)發(fā) 后, maybe rvalue 變 lvalue

void f2(int&& para2) {}
void f2(int& para2) {]

template <typename T>
void f1(T&& para1) { f2(para1); }

// arg: lvalue -> 應調(diào) f1(int& para1) => T = int&
//  -> para1 作 arg: lvalue -> 調(diào) f2(int&)
f1(val);

// arg: rvalue -> 應調(diào) f1(int&& para1) => T = int
//  -> para1 作 arg: lvalue -> 調(diào) f2(int&)
f1(0); 

(3) universal references + 完美轉(zhuǎn)發(fā)

void f2(int&& para2) { }
void f2(int&  para2) { }

// arg (val) lvalue -> 應調(diào) f1(int& para1) => T = int& -> + && = int&
//      -> ...para1 作 arg: value = lvalue -> 調(diào) f2(int& para2)

// arg: (val) rvalue -> 應調(diào) f1(int&& para1) => T = int -> + && = int&&
//      -> ...para1 作 arg: value = rvalue -> 調(diào) f2(int&& para2)
template <typename T>
void f1(T&& para1)
{
    f2( std::forward<T>(para1) ); // 按參數(shù) val 的 value 實際類型 轉(zhuǎn)發(fā)
}

int val = 0;
f1(val); 
f1(0);

5 std::move() C++11

`1. 背景`

對象 含 move 語義 的 func

std::string s1 = "hello";
std::string s2;

(1) 右值 obj 作 arg: 隱含調(diào) move=

// ctor + move=: string& operator=(srting&&);
s2 = std::string("world"); 

(2) 左值 obj 直接作 arg: 匹配調(diào) copy=

s2 = s1; // copy= : string& operator = (const string& );

(3) 想 左值 obj 作 arg : 匹配調(diào) move=

solu: 

std::move wrap 左值 obj 為 右值引用 -> 作 arg: 以 匹配調(diào) move=

// string& operator=(const string&&);
s2 = std::move(s1); 
=>

解決的問題:

wrap 左值 obj 為 右值引用 -> 作 arg: 以 匹配調(diào)用 obj_class 的 move 語義 func, 以 move 左值 obj 的 internal handle / heap memory / dynamic array

因為 左值 obj 直接 作 arg, 匹配調(diào)用的是 copy 語義 func

`2. 機制`

將 arg ( 左值/右值 obj ) 強轉(zhuǎn)為 右值引用

3. 具有 move 語義條件

對象 含 handle / heap memory / dynamic array + move 語義 的 func

否則, std::move() 是 `copy 語義`
=>

移動語義 的 地方 應該總是用 std::move 將 obj ( 可能是 temp_object ) 轉(zhuǎn)換為 右值引用 -> 沒 move 函數(shù) 時, std::move 默認 copy 操作

void 
pop(T& value)
{
    // ..
    value = std::move( in_stk.top() ); // copy / move 語義
    in_stk.pop(); // destory T 型 data_item 本身
}

top: pass by reference
pop: destory

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者份乒。
  • 序言:七十年代末耘分,一起剝皮案震驚了整個濱河市厂捞,隨后出現(xiàn)的幾起案子宣脉,更是在濱河造成了極大的恐慌表牢,老刑警劉巖蝠检,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辣苏,死亡現(xiàn)場離奇詭異及穗,居然都是意外死亡摧茴,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門埂陆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來苛白,“玉大人,你說我怎么就攤上這事猜惋⊥璺眨” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵著摔,是天一觀的道長缓窜。 經(jīng)常有香客問我,道長谍咆,這世上最難降的妖魔是什么禾锤? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮摹察,結(jié)果婚禮上恩掷,老公的妹妹穿的比我還像新娘。我一直安慰自己供嚎,他們只是感情好黄娘,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著克滴,像睡著了一般逼争。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上劝赔,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天誓焦,我揣著相機與錄音,去河邊找鬼着帽。 笑死杂伟,一個胖子當著我的面吹牛移层,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赫粥,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼观话,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了傅是?” 一聲冷哼從身側(cè)響起匪燕,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤蕾羊,失蹤者是張志新(化名)和其女友劉穎喧笔,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體龟再,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡书闸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了利凑。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浆劲。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖哀澈,靈堂內(nèi)的尸體忽然破棺而出牌借,到底是詐尸還是另有隱情,我是刑警寧澤割按,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布膨报,位于F島的核電站,受9級特大地震影響适荣,放射性物質(zhì)發(fā)生泄漏现柠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一弛矛、第九天 我趴在偏房一處隱蔽的房頂上張望够吩。 院中可真熱鬧,春花似錦丈氓、人聲如沸周循。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽湾笛。三九已至,卻和暖如春该编,著一層夾襖步出監(jiān)牢的瞬間迄本,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工课竣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嘉赎,地道東北人置媳。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像公条,于是被迫代替她去往敵國和親拇囊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354