C++11 & C++14 & C++17新特性


C++11:C++11包括大量的新特性:包括lambda表達式,類型推導關鍵字auto、decltype,和模板的大量改進。

新的關鍵字

auto

C++11中引入auto第一種作用是為了自動類型推導

auto的自動類型推導,用於從初始化表達式中推斷出變量的數據類型。通過auto的自動類型推導,可以大大簡化我們的編程工作

auto實際上實在編譯時對變量進行了類型推導,所以不會對程序的運行效率造成不良影響

另外,似乎auto並不會影響編譯速度,因為編譯時本來也要右側推導然后判斷與左側是否匹配

  1. auto a; // 錯誤,auto是通過初始化表達式進行類型推導,如果沒有初始化表達式,就無法確定a的類型  
  2. auto i = 1;  
  3. auto d = 1.0;  
  4. auto str = "Hello World";  
  5. auto ch = 'A';  
  6. auto func = less<int>();  
  7. vector<int> iv;  
  8. auto ite = iv.begin();  
  9. auto p = new foo() // 對自定義類型進行類型推導 

auto不光有以上的應用,它在模板中也是大顯身手,比如下例這個加工產品的例子中,如果不使用auto就必須聲明Product這一模板參數:

  1. template <typename Product, typename Creator>  
  2. void processProduct(const Creator& creator) {  
  3.     Product* val = creator.makeObject();  
  4.     // do somthing with val  
  5. }         
  6.         . 

如果使用auto,則可以這樣寫:

  1. template <typename Creator>  
  2. void processProduct(const Creator& creator) {  
  3.     auto val = creator.makeObject();  
  4.     // do somthing with val  

拋棄了麻煩的模板參數,整個代碼變得更加正解了。

decltype

decltype實際上有點像auto的反函數,auto可以讓你聲明一個變量,而decltype則可以從一個變量或表達式中得到類型,有實例如下:

  1. int x = 3;  
  2. decltype(x) y = x; 

有人會問,decltype的實用之處在哪里呢,我們接着上邊的例子繼續說下去,如果上文中的加工產品的例子中我們想把產品作為返回值該怎么辦呢?我們可以這樣寫:

  1. template <typename Creator>  
  2. auto processProduct(const Creator& creator) -> decltype(creator.makeObject()) {  
  3.     auto val = creator.makeObject();  
  4.     // do somthing with val  

nullptr

nullptr是為了解決原來C++中NULL的二義性問題而引進的一種新的類型,因為NULL實際上代表的是0,

  1. void F(int a){  
  2.     cout<<a<<endl;  
  3. }  
  4.  
  5. void F(int *p){  
  6.     assert(p != NULL);  
  7.  
  8.     cout<< p <<endl;  
  9. }  
  10.  
  11. int main(){  
  12.  
  13.     int *p = nullptr;  
  14.     int *q = NULL;  
  15.     bool equal = ( p == q ); // equal的值為true,說明p和q都是空指針  
  16.     int a = nullptr; // 編譯失敗,nullptr不能轉型為int  
  17.     F(0); // 在C++98中編譯失敗,有二義性;在C++11中調用F(int)  
  18.     F(nullptr);  
  19.  
  20.     return 0;  

序列for循環

在C++中for循環可以使用類似java的簡化的for循環,可以用於遍歷數組,容器,string以及由begin和end函數定義的序列(即有Iterator),示例代碼如下:

  1. map<string, int> m{{"a", 1}, {"b", 2}, {"c", 3}};  
  2. for (auto p : m){  
  3.     cout<<p.first<<" : "<<p.second<<endl;  

Lambda表達式

lambda表達式類似Javascript中的閉包,它可以用於創建並定義匿名的函數對象,以簡化編程工作。Lambda的語法如下:

[函數對象參數](操作符重載函數參數)->返回值類型{函數體}

  1. vector<int> iv{5, 4, 3, 2, 1};  
  2. int a = 2, b = 1;  
  3.  
  4. for_each(iv.begin(), iv.end(), [b](int &x){cout<<(x + b)<<endl;}); // (1)  
  5.  
  6. for_each(iv.begin(), iv.end(), [=](int &x){x *= (a + b);});     // (2)  
  7.  
  8. for_each(iv.begin(), iv.end(), [=](int &x)->int{return x * (a + b);});// (3) 
  • []內的參數指的是Lambda表達式可以取得的全局變量。(1)函數中的b就是指函數可以得到在Lambda表達式外的全局變量,如果在[]中傳入=的話,即是可以取得所有的外部變量,如(2)和(3)Lambda表達式
  • ()內的參數是每次調用函數時傳入的參數
  • ->后加上的是Lambda表達式返回值的類型,如(3)中返回了一個int類型的變量
  • 下面是各種變量截取的選項:
    • [] 不截取任何變量
    • [&} 截取外部作用域中所有變量,並作為引用在函數體中使用
    • [=] 截取外部作用域中所有變量,並拷貝一份在函數體中使用
    • [=, &foo]   截取外部作用域中所有變量,並拷貝一份在函數體中使用,但是對foo變量使用引用
    • [bar]   截取bar變量並且拷貝一份在函數體重使用,同時不截取其他變量
    • [this]            截取當前類中的this指針。如果已經使用了&或者=就默認添加此選項。

變長參數的模板

我們在C++中都用過pair,pair可以使用make_pair構造,構造一個包含兩種不同類型的數據的容器。比如,如下代碼:

  1. auto p = make_pair(1, "C++ 11"); 

由於在C++11中引入了變長參數模板,所以發明了新的數據類型:tuple,tuple是一個N元組,可以傳入1個, 2個甚至多個不同類型的數據

  1. auto t1 = make_tuple(1, 2.0, "C++ 11");  
  2. auto t2 = make_tuple(1, 2.0, "C++ 11", {1, 0, 2}); 

這樣就避免了從前的pair中嵌套pair的丑陋做法,使得代碼更加整潔

另一個經常見到的例子是Print函數,在C語言中printf可以傳入多個參數,在C++11中,我們可以用變長參數模板實現更簡潔的Print

  1. template<typename head, typename... tail>  
  2. void Print(Head head, typename... tail) {  
  3.     cout<< head <<endl;  
  4.     Print(tail...);  

Print中可以傳入多個不同種類的參數,如下:

  1. Print(1, 1.0, "C++11"); 

更加優雅的初始化方法

在引入C++11之前,只有數組能使用初始化列表,其他容器想要使用初始化列表,只能用以下方法:

  1. int arr[3] = {1, 2, 3}  
  2. vector<int> v(arr, arr + 3); 

在C++11中,我們可以使用以下語法來進行替換:

  1. int arr[3]{1, 2, 3};  
  2. vector<int> iv{1, 2, 3};  
  3. map<int, string>{{1, "a"}, {2, "b"}};  
  4. string str{"Hello World"}; 

然后呢…

如果你想了解更多C++11令人興奮的新特性,我會向你推薦這兩個博客:

胡健的C++11系列博文

ToWrting的C++11系列博文

C++11的編譯器支持列表

原文鏈接:http://my.oschina.net/wangxuanyihaha/blog/183151

C++14:C++14的主要特性可以分為三個領域:Lambda函數、constexpr和類型推導。

Lambda函數

C++14的泛型Lambda使編寫如下語句成為可能:

auto lambda = [](auto x, auto y) {return x + y;};

而另一方面,C++11要求Lambda參數使用具體的類型聲明,比如:

auto lambda = [](int x, int y) {return x + y;};

此外,新標准中的std::move函數可用於捕獲Lambda表達式中的變量,這是通過移動對象而非復制或引用對象實現的:

std::unique_ptr ptr(new int(10)); auto lambda = [value = std::move(ptr)] {return *value;};

constexpr

在C++11中,使用constexpr聲明的函數可以在編譯時執行生成一個值,用在需要常量表達式的地方,比如作為初始化模板的整形參數。C++11的constexpr函數只能包含一個表達式,C++14放松了這些限制,支持諸如if 和switch等條件語句,支持循環,其中包括基於區間(range)的for 循環。

類型推導

C++11僅支持Lambda函數的類型推導,C++14對其加以擴展,支持所有函數的返回類型推導:

auto DeducedReturnTypeFunction();

因為C++14是強類型語言,有些限制需要考慮:

  • 如果一個函數的實現中有多個返回語句,這些語句一定要推導出同樣的類型。
  • 返回類型推導可以用在前向聲明中,但是在使用它們之前,翻譯單元中必須能夠得到函數定義。
  • 返回類型推導可以用在遞歸函數中,但是遞歸調用必須以至少一個返回語句作為先導,以便編譯器推導出返回類型。

C++14帶來的另一個類型推導方面的改進是decltype(auto)語法,它支持使用與auto同樣的機制計算給定表達式的類型。auto和 decltype在C++11中就已經出現了,但是它們在推導類型時使用了不同的機制,這可能會產生不同的結果。

C++14中的其他改變包括可以聲明變量模板,支持使用0b或0B前綴來聲明二進制字面常量。InfoQ已經介紹過C++14中可能破壞C++11程序的其他小型修改

主流C++編譯器對新語言特性的支持正在有條不紊地開發:Clang“完全實現了當前草案的所有內容”;GCCVisual Studio也對C++14的新特性提供了一些支持。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM