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 中被加入語言核心的特性。

1.1 泛型的 lambda

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

auto lambda = (auto x, auto y) {return x + y;}

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

struct unnamed_lambda

{

template

auto operator (T x, U y) const {return x + y;}

};

auto lambda = unnamed_lambda ;

1.2 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 被建立（而不是被呼叫）時求值。 使用引用捕獲可以對被引用的外部閉包的變數使用別名。

auto x = 1;

auto f = &r = x, x = x * 10 {

++r;

return r + x;

};

f ; //sets x to 2 and returns 12

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

auto lambda = value = 1 {return value;}

lambda 函式 lambda 的返回值是 1，說明 value 被初始化為 1。被宣告的捕獲變數的型別會根據初始化表示式推斷，推斷方式與用 auto 宣告變數相同。

使用標準函式 std::move 可以使之被用以透過 move 捕獲：

auto ptr = std::makeunique (10); //See below for std::makeunique

auto lambda = value = std::move (ptr) {return *value;}

1.3 函式返回型別推導

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

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

auto DeduceReturnType ; // 返回型別由編譯器推斷

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

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

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

auto Correct (int i) {if (i == 1)

return i; // 返回型別被推斷為 int

else

return Correct (i-1)+i; // 正確，可以呼叫

}

auto Wrong (int i)

{if (i != 1)

return Wrong (i-1)+i; // 不能呼叫，之前沒有 return 語句

else

return i; // 返回型別被推斷為 int

}

1.4 另一種型別推斷

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

int i;

int&& f ;

auto x3a = i; //x3a 的型別是 int

decltype (i) x3d = i; //x3d 的型別是 int

auto x4a = (i); //x4a 的型別是 int

decltype ((i)) x4d = (i); //x4d 的型別是 int&

auto x5a = f ; //x5a 的型別是 int

decltype (f ) x5d = f ; //x5d 的型別是 int&&

C++14 增加了 decltype (auto) 的語法。允許 auto 的型別宣告使用 decltype 的規則。也即，允許不必顯式指定作為 decltype 引數的表示式，而使用 decltype 對於給定表示式的推斷規則。

const int x = 0;

auto x1 = x; //int

decltype (auto) x2 = x; //const int

int y = 0;

int& y1 = y;

auto y2 = y1; //int

decltype (auto) y3 = y1; //int&

int&& z = 0;

auto z1 = std::move (z); //int

decltype (auto) z2 = std::move (z); //int&&

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

// Return type is `int`.

auto f (const int& i) {return i;}

// Return type is `const int&`.

decltype (auto) g (const int& i) {return i;}

int CPP

{

// Note: Especially useful for generic code!

int x = 123;

staticassert (std::issame::value == 0);

staticassert (std::issame::value == 1);

staticassert (std::issame::value == 1);

return 0;

}

1.5 放鬆的 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。

1.6 變數模板

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

template

constexpr T pi = T (3.141592653589793238462643383);

// 適用於特化規則 :

template

constexpr const char* pi = "pi";

1.7 聚合類成員初始化

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 初始化它。

struct CXX14_aggregate {

int x;

int y = 42;

};

CXX14_aggregate a = {1}; // C++14 允許。a.y 被初始化為 42

1.8 二進位制字面量

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

1.9 數字分位符

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

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

auto integer_literal = 100'0000;

auto floatingpointliteral = 1.797'693'134'862'315'7E+308;

auto binary_literal = 0b0100'1100'0110;

auto silly_example = 1'0'0'000'00;

1.10 屬性

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

deprecated int f ;

deprecated ("g is thread-unsafe. Use h instead")

void g (int& x);

void h (int& x);

void test {int a = f ; // 警告：'f' 已棄用

g (a); // 警告：'g' 已棄用：g is thread-unsafe. Use h instead

}

新的標準庫特性

2.1 共享的互斥體和鎖

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

2.2 元函式的別名

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

template

type_object::type

>::type

>gettypeobject (T&);

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

在 C++14，擁有類型別名的元函式包括：removeconst、removevolatile、removecv、addconst、addvolatile、addcv、removereference、addlvaluereference、addrvaluereference、makesigned、makeunsigned、removeextent、removeallextents、removepointer、addpointer、alignedstorage、alignedunion、decay、enableif、conditional、commontype、underlyingtype、resultof、tuple_element。

template

type_object>>

gettypeobject (T&);

2.3 關聯容器中的異構查詢

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

C++14 允許透過其他型別進行查詢，只需要這個型別和實際的鍵型別之間可以進行比較操作。這允許 std::set 使用 const char*，或任何可以透過 operator 與 std::string 比較的型別作為查詢的引數。

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

2.4 標準自定義字面量

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、std::complex和std::complex複數型別。

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

auto str = "hello world"s; // 自動推導為 std::string

auto dur = 60s; // 自動推導為 chrono::seconds

auto z = 1i; // 自動推導為 complex

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

2.5 透過型別定址多元組

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

tuple t ("foo", "bar", 7);

int i = get (t); //i == 7

int j = get (t); // Same as before: j == 7

string s = get (t); //Compiler error due to ambiguity

2.6 較小的標準庫特性

std::makeunique 可以像 std::makeshared 一樣使用，用於產生 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 標準。

3.1 關於陣列的擴充套件

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

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

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

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

3.2 Optional 值

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

3.3 Concepts Lite

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