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 {
    //...
}

如果名稱空間 ABC 之前沒有被宣告, 那麼這樣的寫法相當於宣告了這些名稱空間. 這就導致了這樣的宣告和 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 進行宣告就必定代表著 beginend 的型別相同. 而這篇 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;
        //...
    }
}

這樣, beginend 的型別就不一定再相同了

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.