C++20 功能特性
提案
指派初始化器
P0329R4
括號形式的聚合體初始化
P0960R3
禁止有使用者宣告建構函式的聚合體
P1008R1
聚合類的類模板實參推導
P1816R0
P2082R1
聚合體
陣列型別
符合以下條件的類型別(常為
struct
或
union
)
沒有私有或受保護的直接
(C++17 起)
非靜態資料成員
沒有使用者宣告的建構函式
(C++11 前)
沒有使用者提供的建構函式(允許顯式預置或棄置的建構函式)
(C++11 起)
(C++17 前)
沒有使用者提供、繼承或 explicit 的建構函式(允許顯式預置或棄置的建構函式)
(C++17 起)
(C++20 前)
沒有使用者宣告或繼承的建構函式
(C++20 起)
沒有虛、私有或受保護
(C++17 起)
的基類
沒有虛成員函式
沒有預設成員初始化器
(C++11 起)
(C++14 前)
聚合體初始化
T
物件
=
{
實參1, 實參2, 。。。
};
(1)
T
物件
{
實參1, 實參2, 。。。
};
(2)
(C++11 起)
T
物件
=
{
.
指派符
=
實參1
,
.
指派符
{
實參2
}
。。。
};
(3)
(C++20 起)
T
物件
{
.
指派符
=
實參1
,
.
指派符
{
實參2
}
。。。
};
(4)
(C++20 起)
T
物件
(
實參1, 實參2, 。。。
);
(5)
(C++20 起)
聚合類的類模板實參推導
在C++17中使用帶CTAD的聚合,我們需要顯式的推導指引,現在沒有必要了。
template
如果有使用者提供了推導指引,則不參與CTAD
#include
可以推導陣列型別
#include
大括號省略不適用於
待決名
的非陣列型別或
待決名
邊界的陣列型別(
待決名dependent name)
#include
適用於包擴充套件。一個擴充套件包的尾隨聚合元素對應於所有剩餘元素
#include
無尾隨元素包展開對應於沒有元素
template
包中的元素數量只被推導一次,但如果重複,型別應該完全匹配:
#include
線上編譯測試
https://wandbox。org/nojs/gcc-headhttps://wandbox。org/nojs/clang-head
禁止使用使用者宣告的建構函式聚合
現在聚合型別不能有使用者宣告的建構函式。以前,聚合只允許有刪除的或預設的建構函式。這導致了帶有預設/刪除建構函式的聚合的怪異行為(它們是使用者宣告的,而不是使用者提供的)
// 以下型別在c++ 20中都不是聚合struct S{ int x{2}; S(int) = delete; // 使用者宣告的建構函式};struct X{ int x; X() = default; // // 使用者宣告的建構函式};struct Y{ int x; Y(); // // 使用者宣告的建構函式};Y::Y() = default;int main(){ S s(1); // 一直都是錯誤 S s2{1}; // C++17正確, C++20錯誤 X x{1}; // C++17正確, C++20錯誤 Y y{2}; // 一直都是錯誤}
https://wandbox。org/permlink/cPA3m3ppHv1h8eIT
線上測試
圓括號的聚會初始化
圓括號的聚合初始化現在與花括號初始化的工作方式相同,但是允許窄化轉換,不允許指定的初始化器,不允許為臨時物件延長生命週期,也不允許大括號省略。沒有初始化式的元素是
值初始化
的。這允許無縫使用工廠函式,如std::make_unique<>()/emplace()。
#include (1, 2, S::S2{3}); // 陣列也支援了 int arr1[](1, 2, 3); int arr2[2](1); // {1, 0}}
https://wandbox。org/permlink/w8OrhnuA6WJLb4GA
指派初始化器
每個
指派符
必須指名 T 的一個直接非靜態資料成員,而表示式中所用的所有
指派符
必須按照與 T 的資料成員相同的順序出現。
struct A { int x; int y; int z; };A a{。y = 2, 。x = 1}; // 錯誤:指派符的順序不匹配宣告順序A b{。x = 1, 。z = 2}; // OK:b。y 被初始化為 0
指派初始化器所指名的每個直接非靜態資料成員,從其指派符後隨的對應花括號或等號初始化器初始化。禁止窄化轉換。
指派初始化器可用於將聯合體初始化為其首個成員之外的狀態。只可以為一個聯合體提供一個初始化器。
union u { int a; const char* b; };u f = { 。b = “asdf” }; // OK:聯合體的活躍成員為 bu g = { 。a = 1, 。b = “asdf” }; // 錯誤:只可提供一個初始化器
對於非聯合體的聚合體中未提供指派初始化器的元素,按上述針對初始化器子句的數量少於成員數量時的規則進行初始化(如果提供預設成員初始化器則使用它,否則為空列表初始化):
struct A { string a; int b = 42; int c = -1;};A{。c=21} // 以 {} 初始化 a,這樣會呼叫預設建構函式 // 然後以 = 42 初始化 b // 然後以 = 21 初始化 c
如果以指派初始化器子句初始化的聚合體擁有一個匿名聯合體成員,那麼對應的指派初始化器必須指名該匿名聯合體的其中一個成員。
注意:亂序的指派初始化、巢狀的指派初始化、指派初始化器與常規初始化器的混合,以及陣列的指派初始化在 C 程式語言中受支援,但在 C++ 不允許。
struct A { int x, y; };struct B { struct A a; };struct A a = {。y = 1, 。x = 2}; // 合法 C,非法 C++(亂序)int arr[3] = {[1] = 5}; // 合法 C,非法 C++(陣列)struct B b = {。a。x = 0}; // 合法 C,非法 C++(巢狀)struct A a = {。x = 1, 2}; // 合法 C,非法 C++(混合)