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`。

```cpp
auto lambda = [](auto x, auto y) {return x + y;}
```

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

```cpp
struct unnamed_lambda
{
  template<typename T, typename U>
    auto operator()(T x, U y) const {return x + y;}
};
auto lambda = unnamed_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被創建（而不是被調用）時求值。使用引用捕獲可以對被引用的外部閉包的變量使用別名。

```cpp
auto x = 1;
auto f = [&r = x, x = x * 10] {
  ++r;
  return r + x;
};
f(); // sets x to 2 and returns 12
```

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

```cpp
auto lambda = [value = 1] {return value;}
```

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

使用標準函數`std::move`可以使之被用以通過move捕獲：

```cpp
auto ptr = std::make_unique<int>(10); //See below for std::make_unique
auto lambda = [value = std::move(ptr)] {return *value;}
```

### 函數返回類型推導

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

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

```cpp
auto DeduceReturnType();   //返回类型由编译器推断
```

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

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

這樣的函數中可以存在遞歸，但遞歸調用必須在函數定義中的至少一個return語句之後：

```cpp
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
}
```

### 另一種類型推斷

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

```cpp
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`對於給定表達式的推斷規則。

```cpp
    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`。

```cpp
// 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;
    static_assert(std::is_same<const int&, decltype(f(x))>::value == 0);
    static_assert(std::is_same<int, decltype(f(x))>::value == 1);
    static_assert(std::is_same<const int&, decltype(g(x))>::value == 1);
    return 0; 
}
```

### 放鬆的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`精度的值）包括特化在內，通常的模板的規則都適用於變量模板的聲明和定義。

```cpp
template<typename T>
constexpr T pi = T(3.141592653589793238462643383);

// 适用于特化规则 :
template<>
constexpr const char* pi<const char*> = "pi";
```

### 聚合類成員初始化

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++
struct CXX14_aggregate {
    int x;
    int y = 42;
};

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

### 二進位字面量

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

### 數字分位符

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

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

```cpp
auto integer_literal = 100'0000;
auto floating_point_literal = 1.797'693'134'862'315'7E+308;
auto binary_literal = 0b0100'1100'0110;
auto silly_example = 1'0'0'000'00;
```

### deprecated 屬性

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

```cpp
[[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
}
```

## 新的標準庫特性

### 共享的互斥體和鎖

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

### 元函數的別名

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

```cpp
template <class T>
type_object<
  typename std::remove_cv<
    typename std::remove_reference<T>::type
  >::type
>get_type_object(T&);
```

利用類型別名模板，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。

```cpp
template <class T>
type_object<std::remove_cv_t<std::remove_reference_t<T>>>
get_type_object(T&);
```

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

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。

```cpp
auto str = "hello world"s; // 自动推导为 std::string
auto dur = 60s;            // 自动推导为 chrono::seconds
auto z   = 1i;             // 自动推导为 complex<double>
```

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

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

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

```cpp
tuple<string, string, int> t("foo", "bar", 7);
int i = get<int>(t);        // i == 7
int j = get<2>(t);          // Same as before: j == 7
string s = get<string>(t);  //Compiler error due to ambiguity
```

### 較小的標準庫特性

`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已出版。