c++ 20版本

前言

C++20(C++ 編程語言標準 2020 版)將是 C++ 語言一次非常重大的更新,將爲這門語言引入大量新特性。近日,C++ 開發者 Rainer Grimm 正通過一系列博客文章介紹 C++20 的新特性。目前這個系列文章已經更新了兩篇,本篇是第一篇,主要介紹了 C++20 的 Big Four(四大新特性:概念、範圍、協程和模塊)以及核心語言(包括一些新的運算符和指示符)。

在這裏插入圖片描述

C++20 有很多更新,上圖展示了 C++20 更新的概況。下面作者首先介紹 了 C++20 的編譯器支持情況,然後介紹 The Big Four(四大新特性)以及核心語言方面的新特性。

C++20 的編譯器支持

適應新特性的最簡單方法是試用它們。那麼接下來我們就面臨着這個問題:哪些編譯器支持 C++20 的哪些特性?一般來說,cppreference.com/compiler_support_能提供在覈心語言和庫方面的答案。

簡單來說,全新的 GCC、Clang 和 EDG 編譯器能提供對核心語言的最佳支持。此外,MSVC 和 Apple Clang 編譯器也支持許多 C++20 特性。庫方面的情況類似。GCC 在庫方面的支持最好,接下來是 Clang 和 MSVC 編譯器。

在這裏插入圖片描述

四大新特性

概念(concept)

使用模板進行通用編程的關鍵思想是定義能通過各種類型(type)使用的函數和類。但是,在實例化模板時經常會出現用錯類型的問題,其結果通常是幾頁難懂的報錯信息。

現在概念來了,這個問題可以休矣。概念讓你能爲模板編寫要求,而編譯器則可以檢查這個要求。概念革新了我們思考和編寫通用代碼的方式。原因如下:

模板的要求是接口的一部分;
類模板中的函數重載或特殊化可以基於概念進行;
因爲編譯器能夠比較模板參數的要求與實際的模板參數,所以能得到更好的報錯信息。
但是,這還不是全部。

你可以使用預定義的概念,也可以定義你自己的概念;
auto 和概念的用法統一到了一起。你可以不使用 auto,而是使用概念;
如果一個函數聲明使用了一個概念,那麼它會自動變成一個函數模板。由此,編寫函數模板就變得與編寫函數一樣簡單。
下面的代碼片段展示了一個簡單概念 Integral 的定義和使用方式:

template<typenameT>conceptboolIntegral(){returnstd::is_integral<T>::value;}Integralautogcd(Integralautoa,Integralautob){if(b==0)returna;elsereturngcd(b,a%b);}

Integral 這個概念需要 std::is_integral::value 中的類型參數 T。std::is_integral::value 這個函數來自 type-traits 庫,它能在 T 爲整數檢查編譯時間。如果 std::is_integral::value 的值爲 true,則沒有問題。如果不爲 true,則你會收到一個編譯時間報錯。如果你很好奇(你也應該好奇),我的這篇文章介紹了 type-traits 庫:https://www.modernescpp.com/index.php/tag/type-traits。

gcd 算法是基於歐幾里德算法確定最大公約數(greatest common divisor)。我使用了這個縮寫函數模板句法來定義 gcd。gcd 要求其參數和返回類型支持概念 Integral。gcd 是一類對參數和返回值都有要求的函數模板。當我刪除這個句法糖(syntactic sugar)時,也許你能看到 gcd 的真正本質。

下面這段代碼在語義上與 gcd 算法等效:

template<typenameT>requiresIntegral<T>()Tgcd(Ta,Tb){if(b==0)returna;elsereturngcd(b,a%b);}

如果你還沒看到 gcd 的真正本質,過幾周我還會專門發佈一篇介紹概念的文章。

範圍庫(Ranges Library)

範圍庫是概念的首個客戶。它支持的算法滿足以下條件:

可以直接在容器上操作;無需迭代器指定一個範圍;
可以寬鬆地評估;
可以組合。
簡單來說:範圍庫支持函數模式(functional patterns)。

代碼可能比語言描述更清楚。下面的函數用豎線符號展示了函數組成:

#include<vector>#include<ranges>#include<iostream>intmain(){std::vector<int>ints{0,1,2,3,4,5};autoeven=[](inti){return0==i%2;};autosquare=[](inti){returni*i;};for(inti:ints|std::view::filter(even)|std::view::transform(square)){std::cout<<i<<'';//0416}}

even 是一個 lambda 函數,其在 i 爲偶數時返回;lambda 函數 square 則會將 i 映射爲它的平方。其餘的必須從左到右讀取的第 i 個函數組成:for (int i : ints | std::view::filter(even) | std::view::transform(square)). 將過濾器 even 應用於 ints 的每個元素,然後將其餘的每個元素映射爲它們的平方。如果你熟悉函數編程,那麼這讀起來就像一篇散文詩。

協程(Coroutines)

協程是廣義的函數,能在保持狀態的同時暫停或繼續。協程通常用來編寫事件驅動型應用。事件驅動型應用可以是模擬、遊戲、服務器、用戶接口或算法。協程也通常被用於協作式多任務(cooperative multitasking)。

我們這裏不介紹 C++20 的具體協程,而會介紹編寫協程的框架。編寫協程的框架由 20 多個函數構成,其中一部分需要你去實現,另一部分則可能需要重寫。因此,你可以根據需求調整協程。

下面展示了一個特定協程的用法。下面的程序使用了一個能產生無限數據流的生成器:

Generator<int>getNext(intstart=0,intstep=1){autovalue=start;for(inti=0;;++i){co_yieldvalue;//1value+=step;}}intmain(){std::cout<<std::endl;std::cout<<"getNext():";autogen=getNext();for(inti=0;i<=10;++i){gen.next();//2std::cout<<""<<gen.getValue();}std::cout<<"\n\n";std::cout<<"getNext(100,-10):";autogen2=getNext(100,-10);for(inti=0;i<=20;++i){gen2.next();//3std::cout<<""<<gen2.getValue();}std::cout<<std::endl;}

必須補充幾句。這段代碼只是一個代碼段。函數 getNext 是一個協程,因爲它使用了關鍵字 co_yield。getNext 有一個無限的循環,其會在 co_yield 之後返回 value。調用 next()(註釋的 第 2、3 行)會繼續這個協程,接下來的 getValue 調用會獲取這個值。在 getNext 調用之後,這個協程再一次暫停。其暫停會一直持續到下一次調用 next()。我的這個示例中有一個很大的未知,即 getNext 函數的返回值 Generator。這部分內容很複雜,後面我在寫協程的文章中更詳細地介紹。

使用 Wandbox 在線編譯器,我可以向你展示這個程序的輸出:
在這裏插入圖片描述

模塊(Module)

模塊部分簡單介紹一下就好。模塊承諾能夠實現:

  • 更快的編譯時間;
  • 宏的隔離;
  • 表達代碼的邏輯結構;
  • 不必再使用頭文件(header file);
  • 擺脫醜陋的宏方法。

原文鏈接:https://www.modernescpp.com/index.php/thebigfour

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章