C++14 新特性

一、目錄

一、目錄…
二、新的語言特性…
1、 泛型的 Lambda 函數…
2、 Lambda 捕獲表達式…
3、 函數返回類型推導…
4、 另一種類型推斷…
5、 放鬆的 constexpr 限制…
6、 變量模板…
7、 聚合體成員初始化…
8、 二進制字面值…
9、 數字分位符…
三、新的標準庫特性…
1、 共享的互斥體和鎖…
2、 元函數的別名…
3、 關聯容器中的異構查找…
4、 標準自定義字面值…
5、 通過類型尋址多元組…
6、較小的標準庫特性…
四、 已被移除或是不包含在 C++14 標準的特性…
1、 關於數組的擴展…
2、 Optional 值…
3、 Concepts Lite…

二、新的語言特性

1 、泛型的 Lambda 函數

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

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

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

struct unnamed_lambda
{
template<typename T, typename U>
auto operator()(T x, U y) const {return x + y;}
};

auto lambda = unnamed_lambda();

2 、Lambda 捕獲表達式

C++11 的 lambda 函數通過值拷貝(by copy)或引用(by reference)捕獲(capture)已在外層作用域聲明的變量。這意味着 lambda 的值成員不可以是 move-only 的類型。C++14允許被捕獲的成員用任意的表達式初始化。這既允許了 capture by value-move,也允許了任意聲明 lambda 的成員,而不需要外層作用域有一個具有相應名字的變量。

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

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

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

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

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

聲明 ptr = std::move(ptr)使用了兩次 ptr。第一次使用聲明瞭一個新的變量,但在捕獲部分,這個變量還不在作用域內。所以第二個 ptr 表示之前在 lambda 之外聲明的變量。

3 、函數返回類型推導

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

爲了啓用返回類型推導,函數聲明必須將 auto 作爲返回類型,但沒有 C++11 的後置返回類型說明符:

auto DeduceReturnType(); //返回類型由編譯器推斷

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

使用返回類型推導的函數可以前向聲明,但在定義之前不可以使用。它們的定義在使用它們的翻譯單元(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
}

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)的語法。這允許不必顯式指定作爲 decltype 參數的表達式,而使用 decltype 對於給定表達式的推斷規則。

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

5 、放鬆的 constexpr 限制

C++11 引入了 constexpr 函數的概念,這樣的函數可以在編譯期執行。它們的返回值可以用於期望常量表達式的場合(如模板的非類型參數)。然而 C++11 要求 constexpr 函數只含有一個將被返回的表達式(也可以還含有 static_assert 聲明等其它語句,但允許的語句類型很少)。

C++14 將放鬆這些限制。聲明爲 constexpr 的函數將可以含有以下內容:
 任何聲明,除了:static 或 thread_local 變量、沒有初始化的變量聲明。
 條件分支語句 if 和 switch。goto 是不允許的。
 所有的循環語句,包括基於範圍的 for 循環。
 表達式可以改變一個對象的值,只需該對象的生命期在常量表達式函數內開始。包括對有 constexpr 聲明的任何非 const 非靜態成員函數的調用。

調用非 constexpr 函數仍然是受限的。所以如果使用基於範圍的 for 循環,begin 和 end的重載形式必須自身被聲明爲 constexpr。值得注意的是,std::initializer_list 在本地和全局都具有 constexpr 聲明的 begin/end 函數。

此外,C++11 指出,所有被聲明爲 constexpr 的非靜態成員函數也隱含聲明爲 const(即函數不能修改*this 的值)。這點已經被刪除,非靜態成員函數可以爲非 const。然而,只有當對象的生命期在常量表達式求值中開始,非const的constexpr成員函數纔可以修改類成員。

6 、變量模板

在 C++之前的版本中,模板可以是函數模板或類模板(C++11 引入了類型別名模板)。C++14 現在也可以創建變量模板。包括特化在內,通常的模板的規則都適用於變量模板的聲明和定義。

7 、聚合體成員初始化

C++11 新增 member initializer,這是一個表達式,被應用到類作用域的成員上,如果構造函數沒有初始化這個成員。聚合體的定義被改爲明確排除任何含有 member initializer 的類,因此,他們不允許使用聚合初始化。C++14 將放鬆這一限制,這種類型也允許聚合初始化。如果花括號初始化列表不提供該參數的值,member initializer 會初始化它。

8 、二進制字面值

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

9 、數字分位符

C++14 引入單引號(’)作爲數字分位符號,使得數值型的字面量可以具有更好的可讀性。Ada、D 語言、Java、Perl、Ruby 等程序設計語言使用下劃線(_)作爲數字分位符號,C++之所以不和它們保持一致,是因爲下劃線已被用在用戶自定義的字面量的語法中。

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;

三、新的標準庫特性

1 、共享的互斥體和鎖

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

2 、元函數的別名

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

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作爲 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。

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

3 、關聯容器中的異構查找

C++標準庫定義了四個關聯容器類。set 和 multiset 允許用戶根據一個值在容器中查找對應的的同類型的值。map 和 multimap 容器允許用戶指定鍵(key)和值(value)的類型,根據鍵進行查找並返回對應的值。然而,查找只能接受指定類型的參數,在 map 和 multimap中是鍵的類型,而在 set 和 multiset 容器中就是值本身的類型。C++14 允許通過其他類型進行查找,只需要這個類型和實際的鍵類型之間可以進行比較操作。[17]這允許 std::setstd::string使用 const char*,或任何可以通過 operator< 與 std::string比較的類型作爲查找的參數。爲保證向後兼容性,這種異構查找只在提供給關聯容器的比較器允許的情況下有效。標
準庫的泛型比較器,如 std::less<>與 std::greater<>允許異構查找。

4 、標準自定義字面值

C++11 增加了自定義字面量(user-defined literals)的特性,使用戶能夠定義新的字面量後綴,但標準庫並沒有對這一特性加以利用。C++14 標準庫定義了以下字面量後綴:
 “s”,用於創建各種 std::basic_string 類型。
 “h”、“min”、“s”、“ms”、“us”、“ns”,用於創建相應的 std::chrono::duration 時間間隔。

using namespace std::literals;
std::string str = "hello world"s;
std::chrono::seconds dur = 60s;

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

5 、通過類型尋址多元組

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

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

6 、較小的標準庫特性

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 標準的特性

1 、關於數組的擴展

在 C++11 和之前的標準中,在堆棧上分配的數組被限制爲擁有一個固定的、編譯期確定的長度。這一擴展允許在堆棧上分配的一個數組的最後一維具有運行期確定的長度。運行期確定長度的數組不可以作爲對象的一部分,也不可以具有全局存儲期,他們只能被聲明爲局部變量。運行期確定長度的數組也可以使用 C++11 的基於範圍的 for 循環。

同時還將添加 std::dynarray 類型,它擁有與 std::vector 和 std::array 相似的接口。代表一個固定長度的數組,其大小在運行期構造對象時確定。std::dynarray 類被明顯地設計爲當它被放置在棧上時(直接放置在棧上,或作爲另一個棧對象的成員),可以使用棧內存而是堆內存。

2 、Optional 值

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

3 、Concepts Lite

被 C++11 拒絕後,Concepts 受到徹底的修改。Concepts Lite 是 Concepts 的一個部分,僅包含類型約束,而不含 concept_map 和 axiom。它將在一個單獨的 Technical Specification中發展,並有可能加入 C++17。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章