近日,ISO C++ 委員會正式發布了 C++20 標準,命名為 ISO/IEC 14882:2020,

作為程式員,看到新標準發布總想實戰一番,目前 gcc 10.2 可以支持部分 C++ 20 標準,編譯的時候需要使用編譯選項:-std=c++2a,
Constraints and concepts (約束和概念)
在類模板和函式模板編程中,主要用于對模板引數的結束和限制,這種約束和限制發生在編譯期,編譯錯誤不在那么晦澀難懂了,
在模板編程中,可以限制模板引數的型別或具用某種特性,如:可以限制為整型、數值型、bool型、或必須支持hash特性、或某個類的派生型別等,
在C++20中Concepts是非常重要的概念,模板編程終于有了質的提升,
Concepts
Concepts是requirements的具名集合,concepts需要宣告在命名空間中,語法如下:
template < template-parameter-list >
concept concept-name = constraint-expression;
如下所示:
template<typename T>
concept Hashable = requires(T a) {
{ std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};//宣告了一個名為Hashable的concept
struct meow {};
template<Hashable T>
void f(T); // 約束這個T必須滿足Hashable concept,否則無法編譯通過,
int main() {
f("abc"s); // OK,string是可hash的
f(meow{}); // Error: meow結構體不是可hash的,當然可以讓其支持hash,
}
//
template<typename T>
concept C=sizeof(T)>10;
template<C T>
class test{};
template<C T>
void func(T t);
Constraints
約束是邏輯操作和運算元的序列,它用于指定對模板實參的要求,可在 requires 運算式中出現,也可直接作為concept的主體,
有三種型別的約束:
- 合取(conjunction)
- 析取(disjunction)
- 原子約束(atomic constraint)
如下所示:
template<Incrementable T>
void f(T) requires Decrementable<T>;
template<Incrementable T>
void f(T) requires Decrementable<T>; // OK:重宣告
Requires
requires 用于約束模板引數或具體的引數,
requires 子句
如下所示:
template<typename T>
void f(T&&) requires Eq<T>; // 可作為函式宣告符的最末元素出現
template<typename T> requires Addable<T> // 或在模板形參串列的右邊
T add(T a, T b) { return a + b; }
關鍵詞 requires 必須后隨某個常量運算式(故可以寫為 requires true),但其意圖是使用某個具名概念(如上例),或具名概念的一條合取/析取,或一個 requires 運算式,
運算式必須具有下列形式之一:
- 初等運算式,例如 Swappable、std::is_integral::value、(std::is_object_v && …) 或任何帶括號運算式
- 以運算子 && 連接的初等運算式的序列
- 以運算子 || 連接的前述運算式的序列
requires 運算式
語法如下:
requires { requirement-seq }
requires ( parameter-list(optional) ) { requirement-seq }
parameter-list - 與函式宣告中類似的形參的逗號分隔串列,但不允許默認實參且不能以(并非指定包展開的)省略號結尾,這些形參無存盤期、連接或生存期,它們僅用于輔助進行各個要求的制定,這些形參在 要求序列 的閉 } 前處于作用域中,
requirement-seq - 要求(requirement)的序列,描述于下(每個要求以分號結尾),
requirement-seq中的每個要求必須是下面的四項之一:
- 簡單要求(simple requirement)
- 型別要求(type requirement)
- 復合要求(compound requirement)
- 嵌套要求(nested requirement)
如下所示:
template<typename T>
concept Addable = requires (T x) { x + x; }; // requires 運算式
template<typename T> requires Addable<T> // requires 子句,非 requires 運算式
T add(T a, T b) { return a + b; }
template<typename T>
requires requires (T x) { x + x; } // 隨即的約束,注意關鍵字被使用兩次
T add(T a, T b) { return a + b; }
Modules (模塊)
用于從邏輯上劃分代碼,能夠加快編譯速度,并且與匯入的順序無關(還記得以前由于#include順序的不同導致的編譯錯誤嗎?)
主要有三個關鍵字:
- module:用于宣告一個模塊
- export:用于匯出模塊、函式或類
- import:用于匯入模塊
如下所示:
定義了一個helloworld模塊,匯出了hello函式
//helloworld.cpp
export module helloworld; // module declaration
import <iostream>; // import declaration
export void hello() { // export declaration
std::cout << "Hello world!\n";
}
//main.cpp
import helloworld;
int main()
{
hello();
}
Coroutines(協程)
協程,就是能夠暫停執行然后在接下來的某個時間點恢復執行的函式,C++中的協程是無堆疊的(stack less),使用協程可以方便的撰寫異步代碼(和撰寫同步代碼類似),
主要涉及三個關鍵字:
- co_await
co_await暫停當前協程的執行,直到等待的操作完成后繼續執行,
task<> tcp_echo_server() {
char data[1024];
for (;;) {
std::size_t n = co_await socket.async_read_some(buffer(data)); #與 Python 中的 await 類似
co_await async_write(socket, buffer(data, n));
}
}
上述代碼,在async_read_some()完成后,繼續執行下面的陳述句,在 async_read_some()執行完成之前,暫停執行并讓出控制權,
- co_yield
co_yield暫停執行并回傳一個值,與return不同的是co_yield雖然回傳了值 ,但當前函式沒有終止,
generator<int> iota(int n = 0) {
while(true)
co_yield n++; //與 Python 中的 yield 類似
}
- co_return
co_return 用于結束當前協程的執行并回傳一個值
lazy<int> f() {
co_return 7;
}
當然協程也有一些限制:
- 不能使用變長實參
- 不能使用普通的 return 陳述句,或占位符回傳型別(auto 或 Concept)
- constexpr 函式、建構式、解構式及 main 函式 不能是協程
Ranges(范圍)
提供了處理基于范圍的元素(可簡單理解為容器)的組件及各種配接器,還有一些新的演算法,
主要有如下幾類:
- 基于范圍的訪問器
- 基于范圍的原語
- 基于范圍的concept
- 視圖
- 工廠
- 配接器
詳見頭檔案:
一個簡單的例子:
#include <vector>
#include <ranges>
#include <iostream>
int main()
{
std::vector<int> ints{0,1,2,3,4,5};
auto even = [](int i){ return 0 == i % 2; };
auto square = [](int i) { return i * i; };
for (int i : ints | std::views::filter(even) | std::views::transform(square)) {
std::cout << i << ' ';
}
}
Designated Initializers(指定初始化)
使用{}初始化陣列、類、結構體或聯合等的成員,
struct A{int a;int b;int c;};
A a{.a=10,.b=100,.c=20};
operator<=>
三路比較運算子,形如:
lhs <=> rhs
其行為如下:
(a <=> b) < 0 if lhs < rhs
(a <=> b) > 0 if lhs > rhs
(a <=> b) == 0 if lhs equal rhs
示例如下:
#include <compare>
#include <iostream>
int main() {
double foo = -0.0;
double bar = 0.0;
auto res = foo <=> bar;
if (res < 0)
std::cout << "-0 is less than 0";
else if (res == 0)
std::cout << "-0 and 0 are equal";
else if (res > 0)
std::cout << "-0 is greater than 0";
}
Attributes(特性)
- [[nodiscard( string-literal )]]
忽略回傳值時警告, - [[likely]] 和[[unlikely]]
指示編譯器優化更可能出現的情況或分支,是一種對變數值出現可能性的一種預判,
int f(int i)
{
if (i < 0) [[unlikely]] {
return 0;
}
return 1;
}
- [[no_unique_address]]
用于優化存盤空間,當成員為空的時候可以不占用存盤空間
Others
constexpr 新增對虛函式的支持,
char8_t 用于存盤utf-8的字串,
constinit
強制常量初始化,不可以動態初始化
const char * g() { return "dynamic initialization"; }
constexpr const char * f(bool p) { return p ? "constant initializer" : g(); }
constinit const char * c = f(true); // OK
constinit const char * d = f(false); // error
labmda
不再支持以值的形式默認捕獲引數;
允許以值的形式顯示捕獲this;
支持模板,且支持可變引數;
template <typename... Args>
void foo(Args... args) {
[...xs=args]{
bar(xs...); // xs is an init-capture pack
};
}
std::format
使用{}進行格式化字串,再也不用惡心的stream來拼接了,之前使用過boost的format,同樣好用,
#include <iostream>
#include <format>
int main() {
std::cout << std::format("Hello {}!\n", "world");
}
std::span
span是容器的視圖(即不擁有),提供對連續元素組的邊界檢查訪問,因為視圖不擁有自己的元素,所以構造和復制的成本很低;
std::jthread
新的執行緒類,與std::thread類似,只是功能更強大,支持停止、自動join等
Calendar 和 time zone
endian 用于判斷大小端的列舉
std::make_shared 支持陣列
atomic支持浮點數和smart ptr
std::basic_syncbuf 和std::basic_osyncstream
string 增加starts_with和end_with函式
std::atomic_ref 原子參考
std::to_array 將xxx轉換為std::array
inline namespace
特性概覽
core language features



library features



更多標準功能詳情,大家也可以移步至C++ 官方發布平臺:
- cppreference.com
- open-std.org
草案版本:
- https://github.com/cplusplus/draft/tree/c++20
CSDN認證博客專家
C++
Python
Linux
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/240946.html
標籤:AI
