吐槽一下笔链,經(jīng)典的c++98經(jīng)過c++11和c++14的升級后段只,似乎一下變成了另一種編程語言了。c++11和c++14引入了大量的新特性鉴扫,使我感覺c++的復雜程度一下上升了數(shù)倍赞枕。
這里的很多主題都非常復雜,我僅僅把我自己的理解大致記錄下來,具體內(nèi)容還是要去看看c++主題演講
cv-qualified
在一些資料中經(jīng)晨簧簦看到cv-qualified type之類的詞語谍椅,這里的cv就是const & volatile,即不變和易變修飾符古话,cv-qualified type就是以const或者volatile修飾的類型雏吭。
在使用模版函數(shù)或者auto時,根據(jù)參數(shù)的類型陪踩,編譯器的類型推導會自動地推導出cv-qualified type杖们。
例如:
// 1\. 模版函數(shù)
template<typename T>
void func(T& t) {…}
const int a = 1;
func(a); // 模版函數(shù)參數(shù)的類型成為const int&
// 2\. auto
const int a = 3;
auto& var = a; // var的類型成為const int&
但是,如果模版函數(shù)或者auto不是引用或者指針肩狂,那么得到的類型是去除了cv-qualified的類型摘完。因為此時新的標識符有了獨立的內(nèi)存空間,對于原標識符內(nèi)存空間的修飾當然不應該繼續(xù)生效了傻谁。
左值和右值
C++規(guī)定:凡是能夠通過取地址運算符取得變量在內(nèi)存中地址的孝治,就是左值,否則就是右值审磁√胳可以這樣理解:左值是存在于內(nèi)存中的變量(不論是堆內(nèi)存還是棧內(nèi)存),而右值是僅存在于寄存器中的臨時變量态蒂。
decltype
decltype可以推斷一個標識符的類型杭措,這個類型是編譯時推導出的。
由于lambda表達式的類型是無名的钾恢,因此如果想要獲得lambda表達式的類型手素,只能使用decltype(lambdaExpr)。例如:
auto f = [](int a, int b) -> int
{
return a * b;
};
decltype(f) g = f; // lambda 的類型是獨有且無名的
i = f(2, 2);
j = g(3, 3);
using和typedef
在C++11中瘩蚪,鼓勵使用using來替代原來的typedef定義類型別名泉懦。using強制將類型名稱放到等號左邊,而將類型放到等號右邊疹瘦,提高了可讀性崩哩;此外,using還支持泛型拱礁,而typedef不支持泛型琢锋。
舉一個例子:
typedef int (*myFunc)(int, char*);
using myFunc = int (*)(int, char*);
類型推斷(type deduction)
scott mayer的演講闡明了一些非常容易被忽視或者混淆的基本概念,理解這些基礎是理解C++11和C++14新增內(nèi)容的必要知識呢灶。
https://www.youtube.com/watch?v=wQxj20X-tIU
右值引用 & 移動語義
右值引用和移動語義也是在C++11新增的詞吴超,用于解決對象在右值賦值時會不必要地進行額外操作的問題。此外鸯乃,同樣利用C++0x引用的T&&右值引用這種寫法鲸阻,也可以實現(xiàn)所謂的“通用引用Universal Reference”跋涣,從而實現(xiàn)完美轉(zhuǎn)發(fā)。
C++從一開始就規(guī)定了引用的語義鸟悴,不過卻沒有提到“左值”或者“右值”引用陈辱,事實上,左值引用正是我們一直以來在C++中使用的引用细诸,過去的C++標準沒有規(guī)定右值引用沛贪,因此過去一直無法使用右值引用。C++0x中規(guī)定:右值引用能夠通過兩個引用符號表示震贵,如果一個函數(shù)的參數(shù)類型是T&&利赋,那么這個函數(shù)就會匹配右值引用參數(shù)。使用右值引用時猩系,我們可以確定右值引用對象在函數(shù)被調(diào)用完成后就會被丟棄媚送,因此可以直接將右值引用對象資源“移動”到this對象中而不用擔心這樣做會導致右值引用對象的狀態(tài)遭到破壞(因為之后這個對象就用不到了,會被立即銷毀)寇甸。
如上所述塘偎,使用右值引用時,能夠確保右值引用對象會在隨后被銷毀拿霉,因此可以將右值引用對象的資源直接“移動”到當前對象中吟秩,“移動”就是指將資源淺拷貝到當前對象,然后將右值引用對象中的資源引用置空友浸。這樣峰尝,原本由右值引用持有的資源就移動到了當前對象中。這就是移動語義收恢。
C++0x同時在標準庫中新增了std::move,move將參數(shù)轉(zhuǎn)為右值引用并返回祭往,而forward根據(jù)參數(shù)是左值還是右值返回對應的引用伦意。
通用引用 & 完美轉(zhuǎn)發(fā)
通用引用其實就是右值引用,但是scott mayer認為右值引用不準確硼补,因為有時候“右值引用”引用的其實不是一個右值驮肉,因此他堅持使用“通用引用”而不是“右值引用”。
以下列舉通用引用的例子:
// 1\. T由編譯器進行類型推倒
template <typename T>
void function(T&& t);
// 2\. 使用auto已骇,auto的推導規(guī)則其實和范型方法一樣离钝,因此也可以被歸為上面一類
auto&& t;
完美轉(zhuǎn)發(fā)(perfect forwarding)問題是指函數(shù)模板在向其他函數(shù)傳遞參數(shù)時該如何保留該參數(shù)的左右值屬性的問題。
也就是說函數(shù)模板在向其他函數(shù)傳遞自身形參時褪储,如果相應實參是左值卵渴,它就應該被轉(zhuǎn)發(fā)為左值;同樣如果相應實參是右值鲤竹,它就應該被轉(zhuǎn)發(fā)為右值浪读。
這樣做是為了讓其他函數(shù)能夠針對轉(zhuǎn)發(fā)而來的參數(shù)的左右值屬性進行不同處理。
參考上面提到的通用引用的例子1,此時不論T&&被推導為左值引用還是右值引用碘橘,實際上的t都會變成左值引用互订,因為在函數(shù)內(nèi)的參數(shù)引用有了名字,能夠被引用了痘拆,從而不符合右值的定義仰禽。但是,編譯器會記住t實際上是個右值纺蛆,如果希望獲取t原本的引用類型坟瓢,就需要使用新增的std::forward方法來獲取引用的實際類型。
lambda
auto
auto是自動類型推導犹撒,可以根據(jù)表達式的返回自動推導出變量的類型折联。可以使用const & volatile以及引用&指針來修飾auto類型標識识颊。
std::initializer_list
c++11標準庫中新增了一個重要的類型定義:
template< class T >
class initializer_list;
一個initializer_list<T>是一個輕量級的代理對象诚镰,可以通過它訪問一組類型為T的數(shù)組。
在以下情況下祥款,initializer_list會被自動創(chuàng)建:
// class MyClass有一個接受std::initializer_list的構造函數(shù)
MyClass a({1, 2, 3});
MyClass a = {1, 2, 3};
for (auto& a : {1, 2, 3}) {...}