左值右值

基本概念:

左值:在內存中有可以訪問的地址,對象是一個左值蹋艺。

右值:不可以取地址,整數10是個右值黄刚。

引用:對象的別名捎谨,沒有創(chuàng)建新的對象,僅僅給已經存在的對象賦予了一個新的名字憔维。

  1. 引用是對象的別名涛救,對于引用的一切操作都是對對象的操作;

  2. 引用自身從概念上沒有大幸蛋恰(或者就是對象的大屑爝骸);但引用在傳遞或需要存儲時凶赁,其傳遞或存儲的大小為地址的大小咧栗。

  3. 引用必須初始化;

  4. 引用不可能重新綁定虱肄;

  5. 將指針所指向的對象綁定到一個引用時致板,需要確保指針非空。

  6. 任何引用類型的變量咏窿,都是左值斟或。

四種類型引用:

類型 例子 備注
const lvalue refrence Foo foo(10); const Foo& ref = foo;
const rvalue refrence const Foo& ref = Foo(10);
non-const lvalue refrence Foo foo(10); Foo& ref = foo;
non-const rvalue refrence Foo&& ref=Foo(10); C++11才開始有

move語義:

C++11 之前,只有 copy 語意集嵌,這對于極度關注性能的語言而言是一個重大的缺失萝挤。

對move 語意的急迫需求御毅,到了 C++11 終于被引入。其直接的驅動力很簡單:在構造或者賦值時怜珍,如果等號右側是一個中間臨時對象端蛆,應直接將其占用的資源直接 move 過來(對方就沒有了)。

但問題是酥泛,如何讓一個構造函數今豆,或者賦值操作重載函數能夠識別出來這是一個臨時變量?

/////////////hello.cpp/////////////////
#include <iostream>
using namespace std;
struct Foo
{
    Foo(){ cout << "Foo()" << endl; }
    Foo(const Foo&ref){ cout << "Foo(const Foo&)" << endl; } // copy ctor
    Foo(Foo&& ref){ cout << "Foo(Foo&&)" << endl; }          // move ctor
    Foo& operator=(const Foo& rhs){cout << "Foo& operator=(const Foo& rhs)" << endl; } // copy assignment
    Foo& operator=(Foo&& rhs){cout << "Foo& operator=(Foo&& rhs)" << endl; }           // move assignment 
    ~Foo(){ cout << "~Foo()" << endl; }
};

int main(int argc, char* argv[])
{
    cout<<"=========="<<endl;
    Foo foo1 = Foo();
    cout<<"=========="<<endl;
    foo1 = Foo();
    cout<<"=========="<<endl;
    Foo  foo2 = foo1;
    cout<<"=========="<<endl;
    foo2 = foo1;
    getchar();
    return 1;
}

實參類型為non-const lvalue reference柔袁、const lvalue reference呆躲、 const rvalue reference可以匹配到copy ctorcopy assignment

實參類型為non-const rvalue reference 才能匹配到 move ctormove assignment 捶索。

通過這樣的方式插掂,讓 Foo foo1 = Foo()foo1 = Foo()這樣的表達式,都可以匹配到 move 語意的版本腥例。

與此同時辅甥,讓 Foo foo2 = foo1foo2 = foo1 這樣的表達式,依然使用 copy 語意的版本院崇。

達到以上效果需要編譯時加上-fno-elide-constructors肆氓,以此關閉編譯器省略創(chuàng)建一個只是為了初始化另一個同類型對象的臨時對象的優(yōu)化。

root@ubuntu-Vostro-3268:/mnt/zpp# g++ hello.cpp  -fno-elide-constructors
root@ubuntu-Vostro-3268:/mnt/zpp# 
root@ubuntu-Vostro-3268:/mnt/zpp# 
root@ubuntu-Vostro-3268:/mnt/zpp# ./a.out 
==========
Foo()
Foo(Foo&&)
~Foo()
==========
Foo()
Foo& operator=(Foo&& rhs)
~Foo()
==========
Foo(const Foo&)
==========
Foo& operator=(const Foo& rhs)

使用編譯器優(yōu)化時:

root@ubuntu-Vostro-3268:/mnt/zpp# g++ hello.cpp  
root@ubuntu-Vostro-3268:/mnt/zpp# ./a.out 
==========
Foo()
==========
Foo()
Foo& operator=(Foo&& rhs)
~Foo()
==========
Foo(const Foo&)
==========
Foo& operator=(const Foo& rhs)

練習:

struct Foo
{
    Foo(int a) :a(a){}
    int a;
};

void test1(Foo&& f)
{
// 對于任何類型為 右值引用的變量(當然也包括函數參數)底瓣,只能由右值來初始化谢揪;
}

void test2(Foo& f)
{
//  一個右值,不能被 T& 類型的參數匹配捐凭;畢竟這種可以修改的承諾拨扶。而修改一個調用后即消失的臨時
//  對象上,沒有任何意義茁肠,反而會導致程序員犯下潛在的錯誤患民,因而還是禁止了最好
}

void test3(const Foo& f)
{

}   

Foo f1(1);
test1(f1);  // wrong  cannot bind ‘Foo’ lvalue to ‘Foo&&’ 不能將一個左值綁定到右值引用
test2(f1);  // ok
test3(f1);  // ok

test1(Foo{1});  // ok      Foo{1}是右值
test2(Foo{1});  // wrong   這種做法無意義,invalid initialization of non-const reference of type ‘Foo&’ from an rvalue of type ‘Foo’
test3(Foo{ 1 });  // ok

// ref是左值雖然其類型是右值引用; 
// 一個類型為 右值引用的變量,一旦被初始化之后垦梆,臨時對象的生命將被擴展匹颤,會在其被創(chuàng)建的 scope 內始終有效。
// 看似 ref 被定義的類型為 右值引用托猩,但這僅僅約束它的初始化:只能從一個 右值進行初始化印蓖。
// 但一旦初始化完成,它就和一個 左值引用再也沒有任何差別:都是一個已存在對象的 標識京腥。
Foo&& ref = Foo{1}; 
test1(ref);    // wrong  ref是左值,test1的形參為右值引用,右值引用的變量只能由右值來初始化 cannot bind ‘Foo’ lvalue to ‘Foo&&’
test2(ref); // ok  
test3(ref); // ok

速亡值:

只有右值臨時對象可以初始化右值引用變量赦肃,從而也只有右值臨時變量能夠匹配參數類型為 右值引用(T&&)的函數,包括 move 構造函數。

如果程序員就是想把一個左值 move 給另外一個對象他宛,該怎么辦船侧?最簡單的選擇是通過 static_cast 進行類型轉換:

Foo foo{10};           // foo為左值
Foo&& ref = Foo{10};   // ref為左值  類型為右值引用
Foo obj1 = static_cast<Foo&&>(foo); // move 構 造
Foo obj2 = static_cast<Foo&&>(ref); // move 構 造

我們之前說過,只有 右值厅各,才可以用來初始化一個 右值引用類型的變量镜撩,因而也只有 右值才能匹配 move構造。

所以队塘,static_cast<Foo&&>(foo) 表達式琐鲁,肯定是一個 右值

但同時人灼,它返回的類型又非常明確的是一個 引用,而這一點又不符合 右值的定義顾翼。

因為投放,所有的右值,都必須是一個 具體類型适贸,不能是不完備類型灸芳,也不能是抽象類型,但 引用拜姿,無論左值引用烙样,還是右值引用,都可以是不完備類型的引用或抽象類型的引用蕊肥。這是 左值才有的特征谒获。

對于這種既有左值特征,又和右值臨時對象一樣壁却,可以用來初始化右值引用類型的變量的表達式批狱,只能將其歸為新的類別。C++11 給這個新類別命名為 速亡值 (eXpiring value展东,簡稱 xvalue)赔硫。

而將原來的 右值,重新命名為 純右值盐肃。而 速亡值純右值合在一起爪膊,稱為 右值,其代表的含義是砸王,所有可以直接用來初始化 右值引用類型變量的表達式推盛。

同時,由于 速亡值又具備左值特征:可以是不完備類型处硬,可以是抽象類型小槐,可以進行運行時多態(tài)。所以,速亡值又和 左值一起被歸類為 泛左值(generalized lvalue, 簡稱 glvalue)凿跳。

? 類型為 右值引用的變量件豌,只能由 右值表達式初始化;

? 右值包括 純右值速亡值控嗜,其中 速亡值的類型是 右值引用茧彤;

? 類型為 右值引用的變量,是一個 左值疆栏,因而不能賦值給其它類型為 右值引用的變量曾掂,當然也不能匹配參數類型為 右值引用的函數。

參考文獻:

  1. Understanding Modern C++
  2. -fno-elide-constructors參數
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末壁顶,一起剝皮案震驚了整個濱河市珠洗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌若专,老刑警劉巖许蓖,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異调衰,居然都是意外死亡膊爪,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門嚎莉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來米酬,“玉大人,你說我怎么就攤上這事趋箩≡叨睿” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵叫确,是天一觀的道長爬早。 經常有香客問我,道長启妹,這世上最難降的妖魔是什么筛严? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮饶米,結果婚禮上桨啃,老公的妹妹穿的比我還像新娘。我一直安慰自己檬输,他們只是感情好照瘾,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著丧慈,像睡著了一般析命。 火紅的嫁衣襯著肌膚如雪主卫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天鹃愤,我揣著相機與錄音簇搅,去河邊找鬼。 笑死软吐,一個胖子當著我的面吹牛瘩将,可吹牛的內容都是我干的。 我是一名探鬼主播凹耙,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼姿现,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了肖抱?” 一聲冷哼從身側響起备典,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎意述,沒想到半個月后熊经,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡欲险,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了匹涮。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片天试。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖然低,靈堂內的尸體忽然破棺而出喜每,到底是詐尸還是另有隱情,我是刑警寧澤雳攘,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布带兜,位于F島的核電站,受9級特大地震影響吨灭,放射性物質發(fā)生泄漏刚照。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一喧兄、第九天 我趴在偏房一處隱蔽的房頂上張望无畔。 院中可真熱鬧,春花似錦吠冤、人聲如沸浑彰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽郭变。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诉濒,已是汗流浹背周伦。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留循诉,地道東北人横辆。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像茄猫,于是被迫代替她去往敵國和親狈蚤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內容