C++ 17 Paper N3928 《Extending static_assert, v2》導讀
C++ 17 Paper N4086 《Removing trigraphs??!》導讀
C++ 17 Paper N4051 《Allow typename in a template template parameter》導讀
C++ 17 Paper N4267 《Adding u8 character literals》導讀
C++ 17 Paper N4230 《Nested namespace definition》導讀
C++ 17 Paper N4266 《Attributes for namespaces and enumerators》導讀
C++ 17 Paper N4268 《Allow constant evaluation for all non-type template arguments》導讀
C++ 17 Proposal P0001R1 《Remove Deprecated Use of the register Keyword》導讀
C++ 17 Proposal P0002R1 《Remove Deprecated operator++(bool)》導讀
C++ 17 Proposal P0061R1 《__has_include for C++17》導讀
C++ 17 Proposal P0184R0 《Generalizing the Range-Based For Loop》導讀
C++ 17 Proposal P0138R2 《Construction Rules for enum class Values》導讀
C++ 17 Proposal P0245R1 《Hexadecimal floating literals for C++》導讀
C++ 17 Proposal P0028R4 《Using attribute namespaces without repetition》導讀
C++ 17 Proposal P0283R2 《Standard and non-standard attributes》導讀
1.《Extending static_assert, v2》導讀
C++ 11 引入 static_assert
關鍵字之後, 有不少討論都是為什麼 static_assert
必須要給出錯誤指引字串. 有時候為了方便和對付編碼錯誤, 我們可能會這樣去寫 static_assert :
static_assert(constant_boolean_expression, "");
有不少人在討論, 後面的字串應該是可選的. 而這篇 Paper 解決了這個問題, 也就是說, 現在可以這樣去寫 static_assert
:
static_assert(constant_boolean_expression);
2.《Removing trigraphs??!》導讀
在很早之前, 電腦的鍵盤並不能輸入所有程式設計所必須的字元, 因此 C 引入了三字符組
三字符組 | 替換為 |
---|---|
??= |
# |
??/ |
\ |
??' |
^ |
??( |
[ |
??) |
] |
??! |
| |
??< |
{ |
??> |
} |
??- |
~ |
如今, 這些三字符組已經沒有存在的必要了. 所以, 這篇 Paper 提出了移除這些特性. 雖然這篇標題上寫的是三字符組 "??!
", 但是實際上所有的三字符組都被移除了
3.《Allow typename in a template template parameter》導讀
對於樣板參數中的樣板, 在 C++ 17 之前, 我們必須這樣去宣告 :
template <template <typename> class T>
class Foo {};
由於 T
必定是一個樣板類別, 所以之前 C++ 並沒有允許這樣的寫法 :
template <template <typename> typename T>
class Foo {};
而這篇 Paper 提出了解除限制, 允許在樣板參數的樣板宣告中, 使用 typename
4.《Adding u8 character literals》導讀
C++ 20 引入了 char8_t
, 但是在此之前的 C++ 17 中, 這篇 Paper 已經提出了引入了 UTF-8 字串前綴. 為了表示一個 UTF-8 字串, 我們可以在字串前面增加 "u8
" :
const char str[] {u8"abc"};
char c {u8'\\'};
在 C++ 20 之後, 應該這樣去寫 :
const char8_t str[] {u8"abc"};
5.《Nested namespace definition》導讀
對於多重的巢狀名稱空間, 在 C++ 17 之前, 必須明確寫出 :
namespace A {
namespace B {
namespace C {
namespace D {
//...
}
}
}
}
但是, 如果除了最後一個名稱空間中有宣告, 剩餘的名稱空間中都不存在任何宣告, 那麼這樣寫顯得十分繁瑣. 這篇 Paper 提出了簡化版本的寫法 :
namespace A::B::C::D {
//...
}
如果名稱空間 A
、B
和 C
之前沒有被宣告, 那麼這樣的寫法相當於宣告了這些名稱空間. 這就導致了這樣的宣告和 C++ 17 之前的那種寫法完全等價
6.《Attributes for namespaces and enumerators》導讀
屬性這個特性在 C++ 11 被正式引入, 但是它們在 C++ 17 之前暫時無法用於列舉和名稱空間. 這篇 Paper 提出了讓名稱空間和列舉也支援屬性
對於名稱空間來說, 屬性可以這樣去應用 :
namespace [[deprecated]] A {
//...
}
加在 namespace
之後, 名稱空間之前的屬性作用於該名稱空間. 上述宣告表示名稱空間 A
已經被遺棄, 不建議使用. 除了名稱空間之外, 列舉也可以這樣使用屬性, 其作用和名稱空間相同. 另外, 列舉還支援屬性寫在型別之後 :
enum [[deprecated]] A : int [[attr]] {
a, b, c
};
7.《Allow constant evaluation for all non-type template arguments》導讀
對於非型別樣板引數, 其轉型限制是比較多的. 這篇 Paper 提出了解除一些限制 :
- 陣列至陣列指標的轉型
- 函式至函式指標的轉型
- 限定符的轉型
nullptr
向任意指標型別和成員指標型別的轉型
另外, 針對非型別樣板引數, 在 C++ 17 中只要不是
- 子物件 (陣列中的元素, 成員變數)
- 臨時物件
- 字面值字串
typeid
表達式的結果- 預先定義的
__func__
變數
幾乎都可以支援 :
int arr[] {1, 2, 3};
struct S {
int a;
static int b;
} s;
template <int *>
class Foo {};
int main(int argc, char *argv[]) {
Foo<&arr[1]> f1; //Error
Foo<arr> f2; //OK
Foo<&s.a> f3; //Error
Foo<&s.b> f4; //OK
}
8.《Remove Deprecated Use of the register Keyword》導讀
早在 C++ 11 時, register
關鍵字已經被遺棄, 這篇 Proposal 提出從 C++ 中移除這個關鍵字, 作為保留以供未來使用. register
關鍵字是用於標識一個變數 :
register int a = 0;
告訴編碼器這個變數應該放置在暫存器中. 但是現代編碼器幾乎不需要用戶明確標識, 編碼器自身就可以判斷某個變數是否適合存放於某個存儲中. 因此, 這個關鍵字對於 C++ 的影響已經近乎為零
9.《Remove Deprecated operator++(bool)》導讀
這篇 Proposal 旨在提出, 移除早在 C++ 98 中已經被遺棄的特性 : 針對 bool
型別變數的 "++" 運算 :
auto b {false};
b++;
++b;
上述寫法在 C++ 17 中已經不會被編碼器編碼通過
10.《__has_include for C++17》導讀
這篇 Proposal 為 C++ 帶來了一個巨集 : __has_include
. 它用於判定某個標頭檔是否已經被包含 :
#if __has_include(<iostream>)
using std::endl;
#else
#include <iostream>
using std::endl;
#endif
11.《Generalizing the Range-Based For Loop》導讀
這篇 Proposal 實際上是為了 C++ 20 中即將到來的特性 Ranges 準備的. 這篇 Proposal 中提出, 目前的 Range-For 限制過多, 例如尾後疊代器無法進行前進或者後退等的操作. 於是, 這篇 Proposal 提出解除這些限制, 為的就是讓 Ranges 獲得最好的體驗 (在當時還是 Ranges TS). 一個 Range-For 原來會被這樣解釋 :
{
auto &&range {for-range-init};
for(auto begin {begin-init}, end {end-init}; begin not_eq end; ++begin) {
for-range-decl = *begin;
//...
}
}
使用 auto
進行宣告就必定代表著 begin
和 end
的型別相同. 而這篇 Proposal 提出解除限制後, 一個 Range-For 就可能會被這樣解釋 :
{
auto &&range {for-range-init};
auto begin {begin-init};
auto end {end-init};
for(; begin not_eq end; ++begin) {
for-range-decl = *begin;
//...
}
}
這樣, begin
和 end
的型別就不一定再相同了
12.《Construction Rules for enum class Values》導讀
在 C++ 11 引入 enum class
之後, C++ 中就存在一種宣告獨一無二的整型型別的方式 :
enum class E : unsigned long {};
所有型別為 E
的變數都不會向其它整型型別發生轉型. 如果某個時刻, 我們恰好需要這樣的型別以避免程式碼交互過程中的轉型, 那麼使用 enum class
來宣告就非常合適. 但是, 如果需要通過整型型別來宣告 E
的變數, 是需要進行轉型的 :
E a0 = 1; //Error
E a1 {static_cast<E>(1)}; //OK
但是, 如果這樣去寫, 就不需要轉型 :
struct Foo {
unsigned long a;
};
Foo a0 {1}; //OK
Foo a1 = {2}; //OK
這篇 Proposal 除了提出為 enum class
增加建構規則之後, 還避免了上述兩個情況發生衝突. 在原類別建構規則不變的情況下, 允許非縮小轉換下, 利用其它型別來初始化一個限定作用範圍的列舉型別 :
enum class E {};
E func(E) {
return {1}; //Error
}
int main(int argc, char *argv[]) {
E e1 {1}; //OK
E e2 = 1; //Error
E e3 = {1}; //Error
E e4(1); //Error
E e5(E {1}); //OK
E e6 {E {1}}; //OK
func({1}); //Error
func(E {1}); //OK
E *p {new E {1}}; //OK
}
13.《Hexadecimal floating literals for C++》導讀
從 C++ 11 起, 標準樣板程式庫已經支援十六進制的浮點數常數的輸入與輸出, 但是這並不是作為核心語言的部分. 這篇 Proposal 提出, 讓 C++ 支援十六進制的浮點數字面值常數
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
cout << 0x10.1p0 << endl; //輸出結果 : 16.0625
}
其中, p
是一種記數法則, 類似於 e
. 上述程式碼中的 0x10.1p0 = 10.1_{hex} \times 2^{0}
14.《Using attribute namespaces without repetition》導讀
上面我們已經介紹了 C++ 17 為名稱空間引入了屬性, 這篇 Proposal 增加了屬性名稱空間的支援 :
namespace [[attribute namespace::attr]] A {
//...
}
15.《Standard and non-standard attributes》導讀
編碼器通常會支援比 C++ 標準更多的屬性, 因此這篇 Proposal 提出, 對於未知的屬性應該作忽略處理 (一般來說, 編碼器可能會擲出一個警告). 這大大提高了 C++ 程式碼在不同編碼器之間的可攜性. 例如,
int [[unknown_attribute]] a;
[[abc]] int [[ccc]] b [[lll]];
在 Clang 下會產生如下的警告 :
main.cpp:5:11: warning: unknown attribute 'unknown_attribution' ignored [-Wunknown-attributes] int [[unknown_attribution]] a; ^ main.cpp:5:11: warning: unknown attribute 'unknown_attribution' ignored [-Wunknown-attributes] main.cpp:6:19: warning: unknown attribute 'ccc' ignored [-Wunknown-attributes] [[abc]] int [[ccc]] b [[lll]]; ^ main.cpp:6:7: warning: unknown attribute 'abc' ignored [-Wunknown-attributes] [[abc]] int [[ccc]] b [[lll]]; ^ main.cpp:6:19: warning: unknown attribute 'ccc' ignored [-Wunknown-attributes] [[abc]] int [[ccc]] b [[lll]]; ^ main.cpp:6:29: warning: unknown attribute 'lll' ignored [-Wunknown-attributes] [[abc]] int [[ccc]] b [[lll]]; ^ 6 warnings generated.
自創文章, 原著 : Jonny, 如若需要轉發, 在已經授權的情況下請註明出處 :《C++ 17 Paper 導讀合集》https://jonny.vip/2021/02/05/cplusplus-17-paper-%e5%b0%8e%e8%ae%80%e5%90%88%e9%9b%86/
Leave a Reply