《C++0x漫談》系列之:右值引用

字號:

右值引用(及其支持的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,是該類型的臨時對象效率問題就迎刃而解了!