C++14

**C++14** 是 C++ 的現行標準的非正式名稱，正式名稱為 "**International Standard ISO/IEC 14882:2014(E) Programming Language C++**"。C++14 旨在作為 C++11 的一個小擴充，主要提供漏洞修復和小的改進。C++14 標準的委員會草案（Committee Draft）N3690 於 2013 年 5 月 15 日發表。工作草案（Working Draft）N3936 已於 2014 年 3 月 2 日完成。最終的投票期結束於 2014 年 8 月 15 日，結果（一致通過）已於 8 月 18 日公布。

## 新的語言特性

以下為在 C++14 中被加入語言核心的特性。

### 泛型的 lambda

在 C++11 中，lambda 函式的形式參數需要被聲明為具體的類型。C++14 放寬了這一要求，允許 lambda 函式的形式參數聲明中使用類型說明符 `auto`。

泛型 lambda 函式遵循模板參數推導的規則。以上代碼的作用與下面的代碼相同：

### Lambda 擷取部分中使用表達式

C++11 的 lambda 函式通過值拷貝（by copy）或參照（by reference）擷取（capture）已在外層作用域聲明的變數。這意味著 lambda 擷取的變數不可以是 move-only 的類型。

C++14 允許 lambda 成員用任意的被擷取表達式初始化。這既允許了 capture by value-move，也允許了任意聲明 lambda 的成員，而不需要外層作用域有一個具有相應名字的變數。這稱為**廣義擷取**(Generalized capture)。即在擷取子句（capture clause）中增加並初始化新的變數，該變數不需要在 lambda 表達式所處的閉包域（enclosing scope）中存在；即使在閉包域中存在也會被新變數覆蓋 (override)。新變數類型由它的初始化表達式推導。用途是可以從外層作用域中擷取只供移動的變數並使用它。

也即，允許建立 lambda 擷取並用任意表達式初始化，擷取中的名字不需要一定出現在閉包域，初始化表達式在 lambda 被建立（而不是被呼叫）時求值。 使用參照擷取可以對被參照的外部閉包的變數使用別名。

這是通過使用一個初始化表達式完成的：

lambda 函式 `lambda` 的返回值是 1，說明 `value` 被初始化為 1。被聲明的擷取變數的類型會根據初始化表達式推斷，推斷方式與用 `auto` 聲明變數相同。

使用標準函式 `std::move` 可以使之被用以通過 move 擷取：

### 函式返回類型推導

C++11 允許 lambda 函式根據 return 語句的表達式類型推斷返回類型。C++14 為一般的函式也提供了這個能力。C++14 還拓展了原有的規則，使得函式體並不是 `{return _expression_;}` 形式的函式也可以使用返回類型推導。

為了啟用返回類型推導，函式聲明必須將 `auto` 作為返回類型，但沒有 C++11 的後置返回類型說明符：

如果函式實現中含有多個 return 語句，這些表達式必須可以推斷為相同的類型。

使用返回類型推導的函式可以前向聲明，但在定義之前不可以使用。它們的定義在使用它們的翻譯單元（translation unit）之中必須是可用的。

這樣的函式中可以存在遞迴，但遞迴呼叫必須在函式定義中的至少一個 return 語句之後：

### 另一種類型推斷

C++11 中有兩種推斷類型的方式。`auto` 根據給出的表達式產生具有合適類型的變數。`decltype` 可以計算給出的表達式的類型。但是，`decltype` 和 `auto` 推斷類型的方式是不同的。特別地，`auto` 總是推斷出非參照類型，就好像使用了 `std::remove_reference` 一樣，而 `auto&&` 總是推斷出參照類型。然而 `decltype` 可以根據表達式的值類別（value category）和表達式的性質推斷出參照或非參照類型：

C++14 增加了 `decltype(auto)` 的語法。允許 auto 的類型聲明使用 decltype 的規則。也即，允許不必顯式指定作為 `decltype` 參數的表達式，而使用 `decltype` 對於給定表達式的推斷規則。

`decltype(auto)` 的語法也可以用於返回類型推導，只需用 `decltype(auto)` 代替 `auto`。

### 放鬆的 constexpr 函式限制

C++11 引入了聲明為 constexpr 的函式的概念。聲明為 constexpr 函式的意義是：如果其參數均為合適的編譯期常數，則對這個 constexpr 函式的呼叫就可用於期望常數表達式的場合（如模板的非類型參數，或列舉常數的值）。如果參數的值在執行期才能確定，或者雖然參數的值是編譯期常數，但不符合這個函式的要求，則對這個函式呼叫的求值只能在執行期進行。
然而 C++11 要求 constexpr 函式只含有一個將被返回的表達式（也可以還含有 `static_assert` 聲明等其它語句，但允許的語句類型很少）。

C++14 放鬆了這些限制。聲明為 constexpr 的函式可以含有以下內容：

* 任何聲明，除了：
  * `static` 或 `thread_local` 變數。
  * 沒有初始化的變數聲明。
* 條件分支語句 `if` 和 `switch`。
* 所有的迴圈語句，包括基於範圍的 `for` 迴圈。
* 表達式可以改變一個物件的值，只需該物件的生命期在聲明為 constexpr 的函式內部開始。包括對有 `constexpr` 聲明的任何非 `const` 非靜態成員函式的呼叫。

`goto` 仍然不允許在 constexpr 函式中出現。

此外，C++11 指出，所有被聲明為 `constexpr` 的非靜態成員函式也隱含聲明為 `const`（即函式不能修改 \*this 的值）。這點已經被刪除，非靜態成員函式可以為非 `const`。

### 變數模板

在 C++ 之前的版本中，模板可以是函式模板或類別模板（C++11 引入了類型別名模板）。C++14 現在也可以建立變數模板。在提案中給出的範例是變數 `pi`，其可以被讀取以獲得各種類型的 `pi` 的值（例如，當被讀取為整數類型時為 3; 當被讀取為 `float`,`double`,`long double` 時，可以是近似 `float`，`double` 或 `long double` 精度的值）包括特化在內，通常的模板的規則都適用於變數模板的聲明和定義。

### 聚合類別成員初始化

C++11 增加了 default member initializer，如果建構函式沒有初始化某個成員，並且這個成員擁有 default member initializer，就會用 default member initializer 來初始化成員。聚合類（aggregate type）的定義被改為明確排除任何含有 default member initializer 的類類型，因此，如果一個類含有 default member initializer，就不允許使用聚合初始化。

C++14 放鬆了這一限制，含有 default member initializer 的類型也允許聚合初始化。如果在定義聚合體類型的物件時，使用的花括號初始化列表沒有指定該成員的值，將會用 default member initializer 初始化它。

### 二進位字面量

C++14 的數字可以用二進位形式指定。其格式使用字首 `0b` 或 `0B`。這樣的語法也被 Java、Python、Perl 和 D 語言使用。

### 數字分位符

C++14 引入單引號（'）作為數字分位符號，使得數值型的字面量可以具有更好的可讀性。

Ada、D 語言、Java、Perl、Ruby 等程式設計語言使用底線（\_）作為數字分位符號，C++ 之所以不和它們保持一致，是因為底線已被用在使用者自訂的字面量的語法中。

### deprecated 屬性

`deprecated` 屬性允許標記不推薦使用的實體，該實體仍然能合法使用，但會讓使用者注意到使用它是不受歡迎的，並且可能會導致在編譯期間輸出警告訊息。 `deprecated` 可以有一個可選的字串文字作為參數，以解釋棄用的原因和 / 或建議替代者。

## 新的標準庫特性

### 共享的互斥體和鎖

C++14 增加了一類共享的互斥體和相應的共享鎖。起初選擇的名字是 `std::shared_mutex`，但由於後來增加了與 `std::timed_mutex` 相似的特性，`std::shared_timed_mutex` 成為了更適合的名字。

### 元函式的別名

C++11 定義了一組元函式，用於查詢一個給定類型是否具有某種特徵，或者轉換給定類型的某種特徵，從而得到另一個類型。後一種元函式通過成員類型 type 來返迴轉換後的類型，當它們用在模板中時，必須使用 typename 關鍵字，這會增加代碼的長度。

利用類型別名模板，C++14 提供了更便捷的寫法。其命名規則是：如果標準庫的某個類別模板（假設為 `std::_some\_class_`）只含有唯一的成員，即成員類型 type，那麼標準庫提供 `std::_some\_class__t<T>` 作為 `typename std::_some\_class_::type` 的別名。

在 C++14，擁有類型別名的元函式包括：remove\_const、remove\_volatile、remove\_cv、add\_const、add\_volatile、add\_cv、remove\_reference、add\_lvalue\_reference、add\_rvalue\_reference、make\_signed、make\_unsigned、remove\_extent、remove\_all\_extents、remove\_pointer、add\_pointer、aligned\_storage、aligned\_union、decay、enable\_if、conditional、common\_type、underlying\_type、result\_of、tuple\_element。

### 關聯容器中的異構尋找

C++ 標準庫定義了四個關聯容器類。set 和 multiset 允許使用者根據一個值在容器中尋找對應的的同類型的值。map 和 multimap 容器允許使用者指定鍵（key）和值（value）的類型，根據鍵進行尋找並返回對應的值。然而，尋找只能接受指定類型的參數，在 map 和 multimap 中是鍵的類型，而在 set 和 multiset 容器中就是值本身的類型。

C++14 允許通過其他類型進行尋找，只需要這個類型和實際的鍵類型之間可以進行比較操作。這允許 `std::set<std::string>` 使用 `const char*`，或任何可以通過 `operator<` 與 `std::string` 比較的類型作為尋找的參數。

為保證向下相容性，這種異構尋找只在提供給關聯容器的比較器允許的情況下有效。標準庫的泛型比較器，如 `std::less<>` 與 `std::greater<>` 允許異構尋找。

### 標準自訂字面量

C++11 增加了自訂字面量（user-defined literals）的特性，使使用者能夠定義新的字面量字尾，但標準庫並沒有對這一特性加以利用。C++14 標準庫定義了以下字面量字尾：

* "s"，用於建立各種 `std::basic_string` 類型。
* "h"、"min"、"s"、"ms"、"us"、"ns"，用於建立相應的 `std::chrono::duration` 時間間隔。
* "if"、"i"、"il" 用於建立相應的 `std::complex<float>`、 `std::complex<double>` 和 `std::complex<long double>` 複數類型。

這些字面量可用於編譯時的 constexpr。

兩個 "s" 互不干擾，因為表示字串的只能對字串字面量操作，而表示秒的只針對數字。

### 通過類型定址多元組

C++11 引入的 `std::tuple` 類型允許不同類型的值的聚合體用編譯期整型常數索引。C++14 還允許使用類型代替常數索引，從多元組中取得物件。若多元組含有多於一個這個類型的物件，將會產生一個編譯錯誤：

### 較小的標準庫特性

`std::make_unique` 可以像 `std::make_shared` 一樣使用，用於產生 `std::unique_ptr` 物件。

`std::is_final`，用於辨識一個 class 類型是否禁止被繼承。

`std::integral_constant` 增加了一個返回常數值的 `operator()`。

全域 `std::begin/std::end` 函式之外，增加了 `std::cbegin/std::cend` 函式，它們總是返回常數迭代器（constant iterators）。

## 已被移除或是不包含在 C++14 標準的特性

因為 C++14 的主要目的是漏洞修復和小的改進，一些重量級的特性被從 C++14 中移除，其中有部分將加入 C++17 標準。

### 關於陣列的擴充

在 C++11 和之前的標準中，在堆疊上分配的陣列被限制為擁有一個固定的、編譯期確定的長度。這一擴充允許在堆疊上分配的一個陣列的最後一維具有執行期確定的長度。

執行期確定長度的陣列不可以作為物件的一部分，也不可以具有全域儲存期，他們只能被聲明為局部變數。執行期確定長度的陣列也可以使用 C++11 的基於範圍的 for 迴圈。

同時還將添加 `std::dynarray` 類型，它擁有與 `std::vector` 和 `std::array` 相似的介面。代表一個固定長度的陣列，其大小在執行期構造物件時確定。`std::dynarray` 類被明顯地設計為當它被放置在棧上時（直接放置在棧上，或作為另一個棧物件的成員），可以使用棧記憶體而不是堆記憶體。

由於一些設計無法達成一致，這一擴充已被放棄。

### Optional 值

類似於 C# 中的可空類型，optional 類型可能含有或不含有一個值。這一類型基於 Boost 的 `boost::optional` 類，而添加了 C++11 和 C++14 中的新特性，諸如移動和 in-place 構造。它不允許用在參照類型上。這個類被專門的設計為一個 literal type（如果模板參數本身是一個 literal type），因此，它在必要的情況下含有 constexpr 建構函式。

### Concepts Lite

被 C++11 拒絕後，Concepts 受到徹底的修改。Concepts Lite 是 Concepts 的一個部分，僅包含類型約束，而不含 `concept_map` 和 `axiom`。 ISO/IEC TS 19217:2015 Information technology -- Programming languages -- C++ Extensions for concepts 已出版。

## 另見

* C++11
* C++17

## 參考資料