[轉(zhuǎn)]快速了解C/C++的左值和右值

定義###

早期的C給出的定義:左值是一個表達式疮鲫,可能出現(xiàn)在賦值操作的左邊或右邊夺谁,但右值只能出現(xiàn)在右邊。比如:
<pre>
a * b = 42; // 編譯錯誤础芍, 說明 a * b 不是左值
</pre>

因為上面的定義實在太模糊灸叼,導致左值和右值很難被理解神汹,下面給出的定義,更簡單更好理解:左值(lvalue)是一個表達式古今,它表示一個可被標識的(變量或?qū)ο蟮模﹥?nèi)存位置屁魏,并且允許使用&操作符來獲取這塊內(nèi)存的地址。如果一個表達式不是左值捉腥,那它就被定義為右值氓拼。
<pre>
int i = 42;
i = 43;
int* p = &i; // ok, i 是左值
int& foo();
foo() = 42; // ok, foo() 是左值
int* p1 = &foo(); // ok, foo() 是左值

int foobar();
int j = 0;
j = foobar(); // ok, foobar() 是右值
int* p2 = &foobar(); // 錯誤,不能獲取右值的地址
j = 42; // ok, 42 是右值
</pre>

左值與右值之間的轉(zhuǎn)換###

一般上講,對象之間的運算披诗,對象是以右值的形式參與的撬即。比如二元運算符+兩邊的參數(shù)以右值傳入,加后的返回結(jié)果也是右值:
<pre>
int a = 1; // a 是左值
int b = 2; // b 是左值
int c = a + b; // a和b自動轉(zhuǎn)換為右值求和
</pre>

那些表示數(shù)組呈队、函數(shù)和非完整類型的左值是不能轉(zhuǎn)換為右值的剥槐,因為無法對那些類型進行求值。incomplete types指的是類型定義不完整宪摧,只能用指針形式聲明的類型粒竖,在頭文件中經(jīng)常會使用。

左值引用###

C++中可以使用&符定義引用几于,如果一個左值同時是引用蕊苗,就稱為“左值引用”,如:
<pre>
std::string s;
std::string& sref = s; //sref為左值引用
</pre>
非const左值引用不能使用右值對其賦值
<pre>
std::string& r = std::string();//錯誤沿彭!std::string()產(chǎn)生一個臨時對象朽砰,為右值
</pre>
假設(shè)可以的話,就會遇到一個問題:如何修改右值的值喉刘?因為引用是可以后續(xù)被賦值的瞧柔。根據(jù)上面的定義,右值連可被獲取的內(nèi)存地址都沒有睦裳,也就談不上對其進行賦值造锅。

但const左值引用不一樣,因為常量不能被修改廉邑,也就不存在上面的問題:
<pre>
const std::string& r = std::string(); //可以
</pre>

我們經(jīng)常使用const左值引用作為函數(shù)的參數(shù)類型哥蔚,可以減少不必要的對象復制:
<pre>
class MyString
{
public:
MyString &MyString(string& s); //參數(shù)類型為左值引用
};

int main()
{
MyString s1("XXX"); //錯誤
MyString s2(string("XXXX")); //同上,右值不能賦值給左值引用
}
</pre>

帶CV限定符(CV-qualified)的右值###

C++標準中關(guān)于左值轉(zhuǎn)右值的討論蛛蒙,有這樣一段話:

類型為T的左值(非函數(shù)糙箍、非數(shù)組類型)可以被轉(zhuǎn)換為右值。如果T不是類(class)類型宇驾,轉(zhuǎn)換后的右值的類型將為不帶CV限定符的T類型倍靡,否則轉(zhuǎn)換后的右值的類型為T。

什么是CV限定符课舍?如果變量聲明時類型前帶有const或volatile,就說此變量類型具有CV限定符他挎。

在C中筝尾,右值永遠沒有CV限定符,而C++中的類類型的右值可以有CV限定符办桨,看下面代碼:

<pre>
class A
{
public:
void foo() const { std::cout << "A::foo() const\n"; }
void foo() { std::cout << "A::foo()\n"; }
};

A bar() { return A(); } //返回臨時對象筹淫,為右值
const A cbar() { return A(); } //返回帶const的右值(帶CV限定符)

int main()
{
bar().foo(); // 非const對象調(diào)用A::foo()的非const版本
cbar().foo(); // const對象調(diào)用A::foo()的const版本
}
</pre>

也就是說,如果是類類型呢撞,從左值轉(zhuǎn)為右值時损姜,它的CV限定符會被保留饰剥。這里就不給出示例代碼了。

右值引用(C++11)###

右值引用及其相關(guān)的move語義是C++11新引入的最強大的特性之一摧阅。前文說到汰蓉,左值(非const)可以被修改(賦值),但右值不能棒卷。但C++11引入的右值引用特性顾孽,打破了這個限制,允許我們獲取右值的引用比规,并修改之若厚。讓我們先看點代碼: 定義一個類Intvec及其賦值操作符重載函數(shù)如下:
<pre>
class Intvec
{
public:
...
Intvec& operator=(const Intvec& other)
{
log("copy assignment operator");
Intvec tmp(other); //構(gòu)造一個臨時對象,因為other為const蜒什,不能被修改
std::swap(m_size, tmp.m_size);
std::swap(m_data, tmp.m_data);
//跟臨時對象交換值测秸,臨時對象晰構(gòu)時會delete [] m_data
return this;
}
private:
size_t m_size;
int
m_data; //存放int數(shù)組,構(gòu)造時動態(tài)分配
};
</pre>

代碼要點:

  • 代碼使用了copy-swap策略灾常,即先分配資源再更改自身狀態(tài)乞封,這樣可以保證當資源分配失敗的時候,自身能夠維持原先狀態(tài)岗憋,《高效C++》有條規(guī)則描述這個主題肃晚。所以先根據(jù)other拷貝構(gòu)造一個臨時對象tmp,然后與tmp進行swap仔戈,m_data交換給了tmp之后关串,也會隨著tmp的晰構(gòu)而被釋放。
  • 之所以把other聲明為const监徘,有兩個理由晋修,其一是賦值操作不應該更改other,其二是可以傳入一個右值凰盔。其實這樣的聲明隨處可見墓卦。

假設(shè)現(xiàn)有類型為Intvec的對象v,用一個新對象給它賦值:
<pre>
v = Intvec(33);
</pre>

這句代碼合法户敬,它構(gòu)造一個臨時對象落剪,為右值,傳入到Intvec的賦值運算符重載函數(shù)中尿庐。這個代碼是可以工作忠怖,而且通常情況下都比較高效。但是如果Intvec里包含某些m_handle成員抄瑟,創(chuàng)建和釋放m_handle比較昂貴凡泣,那么拷貝構(gòu)造越少越好。這種情況,我們設(shè)想一下鞋拟,如果v能跟Intvec(33)臨時對象直接進行內(nèi)部數(shù)據(jù)交換骂维,而不需要在重載函數(shù)里使用Intvec tmp(other);構(gòu)造一個新對象出來swap,那該有多好贺纲!

如你所料航闺,C++11引入的“右值引用”和“move語義”就可以實現(xiàn)這個目標,新的語法很簡單哮笆,我們重載一個新的賦值操作運算符函數(shù):
<pre>
Intvec& operator=(Intvec&& other)
{
log("move assignment operator");
std::swap(m_size, other.m_size);
std::swap(m_data, other.m_data);
return *this;
}
</pre>
對于v = Intvec(33);這種寫法就會調(diào)用此版本的重載函數(shù)(即傳入一個右值)来颤。
&&語法聲明右值引用,表示一個指向右值的引用稠肘,通過這個引用福铅,可以修改右值。

以上就是關(guān)于右值引用的一個簡單的示例项阴,實際上右值引用是一個復雜的主題滑黔,在實際應用中還有很多場景要考慮。


原文鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末环揽,一起剝皮案震驚了整個濱河市略荡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌歉胶,老刑警劉巖汛兜,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異通今,居然都是意外死亡粥谬,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門辫塌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漏策,“玉大人,你說我怎么就攤上這事臼氨〔粲鳎” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵储矩,是天一觀的道長感耙。 經(jīng)常有香客問我,道長椰苟,這世上最難降的妖魔是什么抑月? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮舆蝴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己洁仗,他們只是感情好层皱,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赠潦,像睡著了一般叫胖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上她奥,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天瓮增,我揣著相機與錄音,去河邊找鬼哩俭。 笑死绷跑,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的凡资。 我是一名探鬼主播砸捏,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼隙赁!你這毒婦竟也來了垦藏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤伞访,失蹤者是張志新(化名)和其女友劉穎掂骏,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體厚掷,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡弟灼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蝗肪。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袜爪。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖薛闪,靈堂內(nèi)的尸體忽然破棺而出辛馆,到底是詐尸還是另有隱情,我是刑警寧澤豁延,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布昙篙,位于F島的核電站,受9級特大地震影響诱咏,放射性物質(zhì)發(fā)生泄漏苔可。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一袋狞、第九天 我趴在偏房一處隱蔽的房頂上張望焚辅。 院中可真熱鬧映屋,春花似錦、人聲如沸同蜻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽湾蔓。三九已至瘫析,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間默责,已是汗流浹背贬循。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留桃序,地道東北人杖虾。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像葡缰,于是被迫代替她去往敵國和親亏掀。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容