C++17部分特性整理
1、使 static_assert 的文本信息可選
- 簡(jiǎn)化和 static_assert的使用,c++17起,消息 可選
static_assert ( 布爾常量表達(dá)式 , 消息 ) (C++11 起)
static_assert ( 布爾常量表達(dá)式 ) (C++17 起)
2侣诺、刪除 trigraphs
- 刪除三元轉(zhuǎn)移字符
- 最初因?yàn)閕so646的標(biāo)準(zhǔn),部分國(guó)家打不出# ~ ^ 之類的字符氧秘,使用??加一個(gè)字符的方式替代年鸳,c++11中不建議使用trigraphs,c++17中被棄用
Trigraph | Equivalent | |
---|---|---|
??= |
# |
|
??/ |
\ |
|
??' |
^ |
|
??( |
[ |
|
??) |
] |
|
??! |
` | ` |
??< |
{ |
|
??> |
} |
|
??- |
~ |
3丸相、在模板參數(shù)中允許使用 typename(作為替代類)
- 在模板聲明中搔确,typename可用作class的代替品,以聲明類型模板形參和模板模板形參(c++17起)
template<typename T> class my_array {};
// 兩個(gè)類型模板形參和一個(gè)模板模板形參:
template<typename K, typename V, template<typename> typename C = my_array>
class Map
{
C<K> key;
C<V> value;
};
4、braced-init-list自動(dòng)推導(dǎo)的新規(guī)則
- 采用大括號(hào)的形式初始化auto變量膳算,可以自動(dòng)完成類型的推導(dǎo)
- 規(guī)則braced-init-list 左側(cè)只能是單個(gè)元素
auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int> 【initializer_list用于表示特定類型值的數(shù)組】
auto x2 = { 1, 2.0 }; // error: cannot deduce element type座硕,會(huì)對(duì)右側(cè)常量進(jìn)行類型檢查
auto x3{ 1, 2 }; // error: not a single element
auto x4 = { 3 }; // decltype(x4) is std::initializer_list<int>
auto x5{ 3 }; // decltype(x5) is int
-
std::initializer_list的簡(jiǎn)單介紹【列表初始化】
std::vector<int>a{1,2,3,4,5}; std::vector<int>a = {1,2,3,4,5}; //vecotr可以使用列表進(jìn)行初始化,因?yàn)閧1,2,3,4,5}被推導(dǎo)為std::initializer_list<int>類型涕蜂,vector的構(gòu)造函數(shù)對(duì)std::initializer_list<int>類型進(jìn)行特化和對(duì)賦值運(yùn)算符的重載進(jìn)行了特化华匾。
[圖片上傳失敗...(image-bff33a-1584675555477)]
5、嵌套命名空間
- 嵌套命名空間定義:namespace A::B::C { ... } 等價(jià)于 namespace A { namespace B { namespace C { ... } } }机隙。
- namespace A::B::inline C { ... } 等價(jià)于 namespace A::B { inline namespace C { ... } }蜘拉。(c++20)inline 可出現(xiàn)于除首個(gè)以外的每個(gè)命名空間名之前
6、允許命名空間和枚舉器的屬性
1有鹿、格式
枚舉格式:
enumerator: identifier attribute-specifier-seqopt
作用域格式:
- original-namespace-definition:
inlineopt namespace attribute-specifier-seqopt identifier { namespace-body }- extension-namespace-definition:
inlineopt namespace attribute-specifier-seqopt original-namespace-name { namespace-body }- unnamed-namespace-definition:
inlineopt namespace attribute-specifier-seqopt { namespace-body }
2旭旭、屬性格式
[[attr]] [[attr1, attr2, attr3(args)]]
3、語(yǔ)法
- [[
屬性列表
]] (c++11)- [[ using
屬性命名空間
:屬性列表
]] (c++17)
4葱跋、屬性實(shí)例
(未找到有關(guān)namesapce和enum相關(guān)的實(shí)例持寄,以函數(shù)屬性列表的使用舉例)
[[gnu::always_inline]] [[gnu::hot]] [[gnu::const]] [[nodiscard]]
inline int f(); // 聲明 f 帶四個(gè)屬性
[[gnu::always_inline, gnu::const, gnu::hot, nodiscard]]
int f(); // 同上,但使用含有四個(gè)屬性的單個(gè)屬性說(shuō)明符
// C++17:
[[using gnu : const, always_inline, hot]] [[nodiscard]]
int f[[gnu::always_inline]](); // 屬性可出現(xiàn)于多個(gè)說(shuō)明符中
int f() { return 0; }
int main() {}
7娱俺、新的標(biāo)準(zhǔn)屬性:
[[fallthrough]]:
僅可應(yīng)用到空語(yǔ)句以創(chuàng)建直落語(yǔ)句(fallthrough statement):[[fallthrough]];稍味。
直落語(yǔ)句僅可用于 switch 語(yǔ)句中,其中待執(zhí)行的下個(gè)語(yǔ)句是該 switch 語(yǔ)句的帶 case 或 default 標(biāo)號(hào)的語(yǔ)句矢否。
-
指示從前一標(biāo)號(hào)直落是有意的仲闽,而在發(fā)生直落時(shí)給出警告的編譯器不應(yīng)診斷它
void f(int n) { void g(), h(), i(); switch (n) { case 1: case 2: g(); [[fallthrough]]; case 3: // 直落時(shí)不警告 h(); case 4: // 編譯器可在發(fā)生直落時(shí)警告【實(shí)際運(yùn)行結(jié)果并未發(fā)生警告】 if(n < 3) { i(); [[fallthrough]]; // OK } else { return; } case 5: [[fallthrough]]; // 非良構(gòu)脑溢,無(wú)后繼的 case 或 default 標(biāo)號(hào) } }
[[nodiscard]]
- 出現(xiàn)在函數(shù)聲明僵朗、枚舉聲明或類聲明中
- 若從并非轉(zhuǎn)型到 void 的棄值表達(dá)式中,調(diào)用聲明為 nodiscard 的函數(shù)屑彻,或調(diào)用按值返回聲明為 nodiscard 的枚舉或類的函數(shù)验庙,則鼓勵(lì)編譯器發(fā)布警告。
struct [[nodiscard]] error_info { };
error_info enable_missile_safety_mode();
void launch_missiles();
void test_missiles() {
enable_missile_safety_mode(); // 編譯器可在舍棄 nodiscard 值時(shí)發(fā)布警告
launch_missiles();
}
error_info& foo();
void f1() {
foo(); // 并非按值返回 nodiscard 類型社牲,無(wú)警告
}
[[maybe_unused]]
- 抑制針對(duì)未使用實(shí)體的警粪薛,此屬性可出現(xiàn)在下列實(shí)體的聲明中:
- 若編譯器針對(duì)未使用實(shí)體發(fā)布警告,則對(duì)于任何聲明為
maybe_unused
的實(shí)體抑制該警告搏恤。
[[maybe_unused]] void f([[maybe_unused]] bool thing1,
[[maybe_unused]] bool thing2)
{
[[maybe_unused]] bool b = thing1 && thing2;
assert(b); // 發(fā)行模式中违寿,assert 在編譯中被去掉,因而未使用 b
// 無(wú)警告熟空,因?yàn)樗宦暶鳛?[[maybe_unused]]
} // 未使用參數(shù) thing1 與 thing2藤巢,無(wú)警告
9、允許所有非類型模板實(shí)參的常量求值
- 附:實(shí)例
//隊(duì)列某個(gè)元素或者非靜態(tài)數(shù)據(jù)成員的地址息罗,對(duì)于非類型模板參數(shù)是不合法的掂咒;
template<int* p> class X { };
int a[10];
struct S { int m; static int s; } s;
X<&a[2]> x3; // error: address of array element
X<&s.m> x4; // error: address of non-static member
X<&s.s> x5; // error: &S::s must be used OK: address of static member
X<&S::s> x6; // OK: address of static member
10、折疊表達(dá)式及在可變長(zhǎng)參數(shù)模板中的使用
折疊表達(dá)式
格式
- 一元左折疊
(pack op ...)
- 一元右折疊
(... op pack)
- 二元右折疊
(pack op ... op I)
- 二元左折疊
(I op ... op pack)
展開
- 一元左折疊展開:
((E1 op E2) op ...) op EN
- 一元右折疊展開:
E1 op (... op (EN-1 op EN))
- 二元右折疊展開:
E1 op (... op (EN-1 op (EN op I)))
- 二元左折疊展開:
(((I op E1) op E2) op ...) op EN
注意
- 若用作 init 或 pack 的表達(dá)式在頂層優(yōu)先級(jí)低于轉(zhuǎn)型淮椰,則它可以加括號(hào):
template<typename ...Args>
int sum(Args&&... args) {
// return (args + ... + 1 * 2); // 錯(cuò)誤:優(yōu)先級(jí)低于轉(zhuǎn)型的運(yùn)算符
return (args + ... + (1 * 2)); // OK
}
- 二元折疊表達(dá)式兩邊的操作數(shù)只能有一個(gè)未展開的的參數(shù)包
template<typename... Args>
bool f(Args... args) {
return (true + ... + args); // OK
}
template<typename... Args>
bool f(Args... args) {
return (args && ... && args); // error: both operands contain unexpanded parameter packs
}
變長(zhǎng)參數(shù)模板參數(shù)解包方法
之前解包方法:
# include <iostream >
template < typename T0 >
void printf (T0 value ) {
std :: cout << value << std :: endl ;
}
template < typename T, typename ... Args >
void printf (T value , Args ... args ) {
std :: cout << value << std :: endl ;
printf ( args ...) ;
}
int main () {
printf (1, 2, "123 ", 1.1) ;
return 0;
}
基于constexpr的解包方法[c++17]
template < typename T0, typename ... T>
void magic(T0 t0, T... t) {
std::cout << t0 << std::endl;
if constexpr (sizeof ...(t) > 0) magic(t ...);
}
11实柠、if constexpr關(guān)鍵字的使用
C++11 引入了constexpr 關(guān)鍵字贾富,它將表達(dá)式或函數(shù)編譯為常量镀钓;
-
C++17 將constexpr 這個(gè)關(guān)鍵字引入到if 語(yǔ)句中架曹,允許在代碼中聲明常量蜓竹,把這一特性引入到條件判斷中去术羔,讓代碼在編譯時(shí)就完成分支判斷姑蓝;
template < typename T> auto print_type_info ( const T& t) { if constexpr (std :: is_integral <T >:: value ) { return t + 1; } else { return t + 0.001; } } int main () { std :: cout << print_type_info (5) << std :: endl ; std :: cout << print_type_info (3.14) << std :: endl ; }
編譯時(shí)展現(xiàn)形式:
int print_type_info ( const int & t) { return t + 1; } double print_type_info ( const double & t) { return t + 0.001; }
12膝蜈、結(jié)構(gòu)化綁定聲明
1刨裆、格式
- attr(可選) cv-auto ref-運(yùn)算符(可選) [ 標(biāo)識(shí)符列表 ] = 表達(dá)式 ;
- attr(可選) cv-auto ref-運(yùn)算符(可選) [ 標(biāo)識(shí)符列表 ] {表達(dá)式 };
- attr(可選) cv-auto ref-運(yùn)算符(可選) [ 標(biāo)識(shí)符列表 ] (表達(dá)式 );
名稱 | 解釋 |
---|---|
attr | 任意數(shù)量的屬性的序列 |
cv-auto | 可有 cv 限定的 auto 類型說(shuō)明符 |
ref-運(yùn)算符 | & 或 && 之一 |
標(biāo)識(shí)符列表 | 此聲明所引入的各標(biāo)識(shí)符的逗號(hào)分隔的列表 |
表達(dá)式 | 頂層沒(méi)有逗號(hào)運(yùn)算符的表達(dá)式(文法上為賦值表達(dá)式),且具有數(shù)組或非聯(lián)合類之一的類型彬檀。 |
2帆啃、綁定情況
2.1綁定到數(shù)組
int a[2] = {1,2};
auto [x,y] = a; // 創(chuàng)建 e[2],復(fù)制 a 到 e窍帝,然后 x 指代 e[0]努潘,y 指代 e[1]
auto& [xr, yr] = a; // xr 指代 a[0],yr 指代 a[1]
2.2綁定到元組式類型
float x{};
char y{};
int z{};
std::tuple<float&,char&&,int> tpl(x,std::move(y),z);
const auto& [a,b,c] = tpl;
// a 指名指代 x 的結(jié)構(gòu)化綁定坤学;decltype(a) 為 float&
// b 指名指代 y 的結(jié)構(gòu)化綁定疯坤;decltype(b) 為 char&&
// c 指名指代 tpl 的第 3 元素的結(jié)構(gòu)化綁定;decltype(c) 為 const int
2.3綁定到數(shù)組成員
struct S {
int x1 : 2;
volatile double y1;
};
S f();
auto [x, y] = f(); // x 是標(biāo)識(shí) 2 位位域的 int 左值
// y 是 volatile double 左值
//根據(jù)測(cè)試結(jié)果 auto [x, y]前不能加cv操作符深浮,否則編譯不過(guò):由于tupule_size無(wú)法識(shí)別
3压怠、注意示項(xiàng)
1、標(biāo)志符的數(shù)量必須等于數(shù)組元素或元組元素或者結(jié)構(gòu)體中非靜態(tài)數(shù)據(jù)成員的數(shù)量飞苇;
13菌瘫、if 和 switch 語(yǔ)句中的變量初始化
-
在if和switch中可以完成初始化,
-
if (
init-statement <u>opt</u> condition)
statement -
if (
init-statement <u>opt</u> condition)
statementelse
statement -
switch (
init-statement <u>opt</u> condition)
statement
-
表達(dá)式展開格式如下:
- if ( init-statement condition ) statement 展開格式如下:
{
? init-statement
? if ( condition ) statement
}
- if ( init-statement condition ) statement else *statemen 展開格式如下:
{
? init-statement
? if ( condition ) statement else statement
}
- switch ( init-statement condition ) statement 展開格式如下:
{
*init-statement*
? switch ( condition ) statement
}
實(shí)例
實(shí)例1
before
status_code foo() {
{
status_code c = bar();
if (c != SUCCESS) {
return c;
}
}
// ...
}
after
status_code foo() {
if (status_code c = bar();c != SUCCESS) {
return c;
}
// ...
}
實(shí)例2
before
void safe_init() {
{
std::lock_guard<std::mutex> lk(mx_);
if (v.empty()){
v.push_back(kInitialValue);
}
}
// ...
}
after
void safe_init() {
if (std::lock_guard<std::mutex> lk(mx_);v.empty()){
v.push_back(kInitialValue);
}
// ...
}