右值引用(及其支持的Move語意和完美轉(zhuǎn)發(fā))是C++0x將要加入的最重大語言特性之一,這點從該特性的提案在C++ - State of the Evolution列表上高居榜首也可以看得出來。從實踐角度講,它能夠完美解決C++中長久以來為人所詬病的臨時對象效率問題。從語言本身講,它健全了C++中的引用類型在左值右值方面的缺陷。從庫設(shè)計者的角度講,它給庫設(shè)計者又帶來了一把利器。從庫使用者的角度講,不動一兵一卒便可以獲得“免費的”效率提升…
Move語意
返回值效率問題——返回值優(yōu)化((N)RVO)——mojo設(shè)施——workaround——問題定義——Move語意——語言支持
大猴子Howard Hinnant寫了一篇挺棒的tutorial(a.k.a. 提案N2027),此外最初的關(guān)于rvalue-reference的若干篇提案的可讀性也相當強。因此要想了解rvalue-reference的話,或者去看C++標準委員會網(wǎng)站上的系列提案(見文章末尾的參考文獻)?;蛘唛喿x本文。
源起
《大史記》總看過吧?
故事,素介個樣子滴…一天,小嗖風風的吹著,在一個伸手不見黑夜的五指…
我用const引用來接受參數(shù),卻把臨時變量一并吞掉了。我用非const引用來接受參數(shù),卻把const左值落下了。于是乎,我就在標準的每個角落尋找解決方案,我靠!我被8.5.3打敗了!…
設(shè)想這樣一段代碼(既然大同小異,就直接從Andrei那篇的文章里面拿來了):
std::vector v = readFile();
readFile()的定義是這樣的:
std::vector readFile()
{
std::vector retv;
… // fill retv
return retv;
}
這段代碼低效的地方在于那個返回的臨時對象。一整個vector得被拷貝一遍,僅僅是為了傳遞其中的一組int,當v被構(gòu)造完畢之后,這個臨時對象便煙消云散。
這完全是公然的浪費!
更糟糕的是,原則上講,這里有兩份浪費。一,retv(retv在readFile()結(jié)束之后便煙消云散)。二,返回的臨時對象(返回的臨時變量在v拷貝構(gòu)造完畢之后也隨即香消玉殞)。不過呢,對于上面的簡單代碼來說,大部分編譯器都已經(jīng)能夠做到優(yōu)化掉這兩個對象,直接把那個retv創(chuàng)建到接受返回值的對象,即v中去。
實際上,臨時對象的效率問題一直是C++中的一個被廣為詬病的問題。這個問題是如此的,以至于標準不惜犧牲原本簡潔的拷貝語意,在標準的12.8節(jié)悍然下詔允許優(yōu)化掉在函數(shù)返回過程中產(chǎn)生的拷貝(即便那個拷貝構(gòu)造函數(shù)有副作用也在所不惜?。_@就是所謂的“Copy Elision”。
為什么(N)RVO((Named) Return Value Optimization)幾乎形同虛設(shè)
還是按照Andrei的說法,只要readFile()改成這樣:
… readFile()
{
if(/* err condition */) return std::vector();
if(/* yet another err condition */) return std::vector(1, 0);
std::vector retv;
… // fill retv
return retv;
}
出現(xiàn)這種情況,編譯器一般都會乖乖放棄優(yōu)化。
但對編譯器來說這還不是最郁悶的一種情況,最郁悶的是:
std::vector v;
v = readFile(); // assignment, not copy construction
這下由拷貝構(gòu)造,變成了拷貝賦值。眼睛一眨,老母雞變鴨。編譯器只能繳械投降。因為標準只允許在拷貝構(gòu)造的情況下進行(N)RVO。
為什么庫方案也不是生意經(jīng)
C++鬼才Andrei Alexandrescu以對C++標準的深度挖掘和利用,早在03年的時候(當時所謂的臨時變量效率問題已經(jīng)在新聞組上鬧了好一陣子了,相關(guān)的語言級別的解決方案也已經(jīng)在02年9月份粉墨登場)就在現(xiàn)有標準(C++98)下硬是折騰出了一個能100%解決問題的方案來。
Andrei把這個框架叫做mojo,就像一層爽身粉一樣,把它往現(xiàn)有類上面一灑,嘿嘿…猜怎么著,不,不是“痱子去無蹤”:P,是該類型的臨時對象效率問題就迎刃而解了!
Move語意
返回值效率問題——返回值優(yōu)化((N)RVO)——mojo設(shè)施——workaround——問題定義——Move語意——語言支持
大猴子Howard Hinnant寫了一篇挺棒的tutorial(a.k.a. 提案N2027),此外最初的關(guān)于rvalue-reference的若干篇提案的可讀性也相當強。因此要想了解rvalue-reference的話,或者去看C++標準委員會網(wǎng)站上的系列提案(見文章末尾的參考文獻)?;蛘唛喿x本文。
源起
《大史記》總看過吧?
故事,素介個樣子滴…一天,小嗖風風的吹著,在一個伸手不見黑夜的五指…
我用const引用來接受參數(shù),卻把臨時變量一并吞掉了。我用非const引用來接受參數(shù),卻把const左值落下了。于是乎,我就在標準的每個角落尋找解決方案,我靠!我被8.5.3打敗了!…
設(shè)想這樣一段代碼(既然大同小異,就直接從Andrei那篇的文章里面拿來了):
std::vector
readFile()的定義是這樣的:
std::vector
{
std::vector
… // fill retv
return retv;
}
這段代碼低效的地方在于那個返回的臨時對象。一整個vector得被拷貝一遍,僅僅是為了傳遞其中的一組int,當v被構(gòu)造完畢之后,這個臨時對象便煙消云散。
這完全是公然的浪費!
更糟糕的是,原則上講,這里有兩份浪費。一,retv(retv在readFile()結(jié)束之后便煙消云散)。二,返回的臨時對象(返回的臨時變量在v拷貝構(gòu)造完畢之后也隨即香消玉殞)。不過呢,對于上面的簡單代碼來說,大部分編譯器都已經(jīng)能夠做到優(yōu)化掉這兩個對象,直接把那個retv創(chuàng)建到接受返回值的對象,即v中去。
實際上,臨時對象的效率問題一直是C++中的一個被廣為詬病的問題。這個問題是如此的,以至于標準不惜犧牲原本簡潔的拷貝語意,在標準的12.8節(jié)悍然下詔允許優(yōu)化掉在函數(shù)返回過程中產(chǎn)生的拷貝(即便那個拷貝構(gòu)造函數(shù)有副作用也在所不惜?。_@就是所謂的“Copy Elision”。
為什么(N)RVO((Named) Return Value Optimization)幾乎形同虛設(shè)
還是按照Andrei的說法,只要readFile()改成這樣:
… readFile()
{
if(/* err condition */) return std::vector
if(/* yet another err condition */) return std::vector
std::vector
… // fill retv
return retv;
}
出現(xiàn)這種情況,編譯器一般都會乖乖放棄優(yōu)化。
但對編譯器來說這還不是最郁悶的一種情況,最郁悶的是:
std::vector
v = readFile(); // assignment, not copy construction
這下由拷貝構(gòu)造,變成了拷貝賦值。眼睛一眨,老母雞變鴨。編譯器只能繳械投降。因為標準只允許在拷貝構(gòu)造的情況下進行(N)RVO。
為什么庫方案也不是生意經(jīng)
C++鬼才Andrei Alexandrescu以對C++標準的深度挖掘和利用,早在03年的時候(當時所謂的臨時變量效率問題已經(jīng)在新聞組上鬧了好一陣子了,相關(guān)的語言級別的解決方案也已經(jīng)在02年9月份粉墨登場)就在現(xiàn)有標準(C++98)下硬是折騰出了一個能100%解決問題的方案來。
Andrei把這個框架叫做mojo,就像一層爽身粉一樣,把它往現(xiàn)有類上面一灑,嘿嘿…猜怎么著,不,不是“痱子去無蹤”:P,是該類型的臨時對象效率問題就迎刃而解了!