C++ feels like a new language. -- Bjarne Stroustrup
- 類型推演
- 右值引用
- 通用引用
- 剖析std::move
- 剖析std::forward
- 總結
類型推演
增強了的「類型系統(tǒng)」是C++11
最大的優(yōu)化亮點之一,為此需要深入剖析「類型推演」的工作機理,并能靈活地運用auto, decltype
,這是C++11
最重要的基石。
template與auto
C++98
早已具備類型推演的能力苔巨,用于模板的類型推演。

在C++11
中,auto
與template
的類型推演能力基本類似魏割,只存在唯一的差異:Braced Initialization
,或稱為Universal Initialization
耙蔑。

非常量的左值引用

需要注意的是见妒,推演auto &r2 = r, auto &cr2 = cr
時,即使r(int&), cr(const int&)
是引用變量甸陌,需要去除引用后再嘗試類型推演须揣,因為使用「引用變量」等價于使用其「引用對象」本身。

常量的左值引用
因為const T&
钱豁,const auto&
已經(jīng)具備了const
的屬性耻卡,當const
的左值對象賦予它所發(fā)生的自動類型推演,其模板參數(shù)T
牲尺,及其auto
的類型無需推演為const
屬性卵酪。


指向非常量的指針
指針的推演能力與引用類似。

需要注意auto *p = &i; auto p = &i
兩種寫法的不一樣谤碳,一種是顯式的指針類型溃卡,另外一種完全依賴于auto
的類型推演能力。

指向常量的指針
與指向非常量的指針推演機制一致蜒简,在此不再冗述瘸羡。


按值傳遞
Pass-By-Value
,經(jīng)過拷貝之后搓茬,兩者之間已無任何瓜葛犹赖,為此const
的處理機制有別于其他情況队他。



但存在兩類特殊的,遺留的C-Style
情況峻村,為保證兼容性麸折,存在特殊的類型推演機制。
遺留的C-style
字符串

遺留的C-style
函數(shù)

通用引用:Universal Reference
所謂Universal Reference
粘昨,因為其能Can bind to anything
垢啼,所以稱為「通用引用」,具有如下方面的特點:
- Can bind to lvalue or rvalue;
- Can bind to const/non-const, volatile/non-volatile, or both;
- So, it can bind to anything.
需要注意的是张肾,Universal Reference
并非「右值引用(Rvalue Reference)」膊夹,即使它們兩者都有類似的T &&
的修飾符。規(guī)則非常簡單捌浩,Universal Reference
具備兩個最基本的特征:
-
T &&, auto&&
: 必須具備的句法結構 -
type reduce
:必須發(fā)生類型推演
可以簡單歸納之放刨,Universal Reference
出現(xiàn)于如下兩種常見:
template <typename T>
void f(T&& t);
auto&& r = i;
Universal Reference
類型推演也存在特殊性:
Universal Reference
持有左值時,發(fā)生Reference Collapsing
機制尸饺。例如auto&& t = i
进统,當auto
推演為int&
,auto&& t
推演為int& && t
浪听,而int& &&
經(jīng)過Reference Collapsing
機制螟碎,被進一步規(guī)約為int&
,與原來它持有左值剛好匹配迹栓。Universal Reference
持有右值時掉分,推演規(guī)則較為直觀,例如auto&& t = 10
克伊,當auto
被推演為int
酥郭,則auto&& t
推演為int&& t
,與原來它持有右值剛好匹配愿吹。



通用初始化:Braced Initialization
這是template
與auto
類型推演能力之間存在的唯一差別不从。

右值引用
「右值引用」(Rvalue Reference)
與「通用引用」(Universal Reference)
是兩個不同的概念,非常容易混淆犁跪,本文試圖揭示兩者之間的本質的差異椿息。
左值與右值
「左值」與「右值」并非C++11
的產(chǎn)物,早已是C++
類型系統(tǒng)的一部分了坷衍,并且兩者之間存在明顯的區(qū)別寝优。

舉個例子,進一步明細兩者之間的差異枫耳。此處使用auto&&
的Universal Reference
乏矾,它會根據(jù)「左值」自動推演為「左值引用」,而「右值」推演為「右值引用」。

右值引用
在C++98
中妻熊,只存在「左值引用」,遺恨缺失「右值引用」的概念仑最,也因此丟失了部分性能優(yōu)化的空間扔役。C++11
中引入了「右值引用」,彌補之前的過失警医,結合「移動」(move)的機制亿胸,進一步提高了C++
在特殊場景的性能。
所謂右值引用预皇,即「右值」的引用侈玄;之前慣稱的「引用」,其實是「左值引用」的簡稱吟温⌒蛳桑「左值引用」只能引用「左值」,「右值引用」只能引用「右值」鲁豪。

通用引用
「通用引用」并非「左值引用」潘悼,即使它們之間都具有&&
的語法結構∨老穑「通用引用」即可以持有「左值」治唤,也可以持有「右值」,是一種「通用」的引用類型糙申。而「右值引用」只能引用「右值」宾添。

特征
-
void f(Object&& o)
,因為未發(fā)生類型推演柜裸,為右值引用 -
template <typename T> void f(std::vector<T>&& v)
缕陕,因為不是T&&
的句法結構,為右值引用
可以簡單歸納之疙挺,「通用引用」出現(xiàn)于如下兩種常見:
template <typename T>
void f(T&& t);
auto&& r = i;

樣例
std::vector
新增加的「右值引用」的push_back
榄檬,及其「通用引用」的emplace_back
是最好的案例。

剖析std::move
C++11實現(xiàn)

當傳遞左值時
經(jīng)過如下的類型推演過程衔统,當傳遞「左值」時鹿榜,std::move
強制轉為換「右值引用」。

當傳遞右值時
經(jīng)過如下的類型推演過程锦爵,當傳遞「右值」時舱殿,std::move
順水推舟隔节,傳遞「右值引用」豌鹤。綜上述,借助「通用引用」的能力妒御,std::move
其實完成了「無條件的」右值引用轉換規(guī)則樟氢。

C++14改進實現(xiàn)

剖析std::forward
C++11實現(xiàn)

當傳遞左值時
經(jīng)過如下的類型推演過程得知冈绊,當傳遞「左值」時侠鳄,std::forward
完成「左值」的「轉發(fā)」機制。

當傳遞右值時
經(jīng)過如下的類型推演過程得知死宣,當傳遞「右值」時伟恶,std::forward
也完成「右值」的「轉發(fā)」機制。為此毅该,std::forward
的機制博秫,完成了C++11
的「完美轉換」(Perfect Forward
)的機制。

C++14改進實現(xiàn)

習慣用法

回顧
