C++14 新特性
一、新的語言特性
1 、泛型的 Lambda 函數(shù)
在 C++11 中,lambda 函數(shù)的形式參數(shù)需要被聲明為具體的類型。C++14 放寬了這一要求,允許 lambda 函數(shù)的形式參數(shù)聲明中使用類型說明符 auto。
auto lambda = [](auto x, auto y) {return x + y;}泛型 lambda 函數(shù)遵循模板參數(shù)推導(dǎo)的規(guī)則。以上代碼的作用與下面的代碼相同:
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 函數(shù)通過值拷貝(by copy)或引用(by reference)捕獲(capture)已在外層作用域聲明的變量。這意味著 lambda 的值成員不可以是 move-only 的類型。C++14允許被捕獲的成員用任意的表達式初始化。這既允許了 capture by value-move,也允許了任意聲明 lambda 的成員,而不需要外層作用域有一個具有相應(yīng)名字的變量。
這是通過使用一個初始化表達式完成的:
auto lambda = [value = 1] {return value;}lambda 函數(shù) lambda 的返回值是 1,說明 value 被初始化為 1。被聲明的捕獲變量的類型會根據(jù)初始化表達式推斷,推斷方式與用 auto 聲明變量相同。
使用標(biāo)準(zhǔn)函數(shù) 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。第一次使用聲明了一個新的變量,但在捕獲部分,這個變量還不在作用域內(nèi)。所以第二個 ptr 表示之前在 lambda 之外聲明的變量。
3 、函數(shù)返回類型推導(dǎo)
C++11 允許 lambda 函數(shù)根據(jù) return 語句的表達式類型推斷返回類型。C++14 為一般的函數(shù)也提供了這個能力。C++14 還拓展了原有的規(guī)則,使得函數(shù)體并不是{return expression;}形式的函數(shù)也可以使用返回類型推導(dǎo)。
為了啟用返回類型推導(dǎo),函數(shù)聲明必須將 auto 作為返回類型,但沒有 C++11 的后置返回類型說明符:
auto DeduceReturnType(); //返回類型由編譯器推斷如果函數(shù)實現(xiàn)中含有多個 return 語句,這些表達式必須可以推斷為相同的類型。[9]
使用返回類型推導(dǎo)的函數(shù)可以前向聲明,但在定義之前不可以使用。它們的定義在使用它們的翻譯單元(translation unit)之中必須是可用的。
這樣的函數(shù)中可以存在遞歸,但遞歸調(diào)用必須在函數(shù)定義中的至少一個 return 語句之后:
auto Correct(int i) {if (i == 1)return i; // 返回類型被推斷為 intelsereturn Correct(i-1)+i; // 正確,可以調(diào)用 }auto Wrong(int i) {if(i != 1)return Wrong(i-1)+i; // 不能調(diào)用,之前沒有 return 語句elsereturn i; // 返回類型被推斷為 int }4 、另一種類型推斷
C++11 中有兩種推斷類型的方式。auto 根據(jù)給出的表達式產(chǎn)生具有合適類型的變量。decltype 可以計算給出的表達式的類型。但是,decltype 和 auto 推斷類型的方式是不同的。
特別地,auto 總是推斷出非引用類型,就好像使用了 std::remove_reference 一樣,而 auto&&總是推斷出引用類型。然而 decltype 可以根據(jù)表達式的值類別(value category)和表達式的性質(zhì)推斷出引用或非引用類型:
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 參數(shù)的表達式,而使用 decltype 對于給定表達式的推斷規(guī)則。
decltype(auto)的語法也可以用于返回類型推導(dǎo),只需用 decltype(auto)代替 auto。
5 、放松的 constexpr 限制
C++11 引入了 constexpr 函數(shù)的概念,這樣的函數(shù)可以在編譯期執(zhí)行。它們的返回值可以用于期望常量表達式的場合(如模板的非類型參數(shù))。然而 C++11 要求 constexpr 函數(shù)只含有一個將被返回的表達式(也可以還含有 static_assert 聲明等其它語句,但允許的語句類型很少)。
C++14 將放松這些限制。聲明為 constexpr 的函數(shù)將可以含有以下內(nèi)容:
? 任何聲明,除了:static 或 thread_local 變量、沒有初始化的變量聲明。
? 條件分支語句 if 和 switch。goto 是不允許的。
? 所有的循環(huán)語句,包括基于范圍的 for 循環(huán)。
? 表達式可以改變一個對象的值,只需該對象的生命期在常量表達式函數(shù)內(nèi)開始。包括對有 constexpr 聲明的任何非 const 非靜態(tài)成員函數(shù)的調(diào)用。
調(diào)用非 constexpr 函數(shù)仍然是受限的。所以如果使用基于范圍的 for 循環(huán),begin 和 end的重載形式必須自身被聲明為 constexpr。值得注意的是,std::initializer_list 在本地和全局都具有 constexpr 聲明的 begin/end 函數(shù)。
此外,C++11 指出,所有被聲明為 constexpr 的非靜態(tài)成員函數(shù)也隱含聲明為 const(即函數(shù)不能修改*this 的值)。這點已經(jīng)被刪除,非靜態(tài)成員函數(shù)可以為非 const。然而,只有當(dāng)對象的生命期在常量表達式求值中開始,非const的constexpr成員函數(shù)才可以修改類成員。
6 、變量模板
在 C++之前的版本中,模板可以是函數(shù)模板或類模板(C++11 引入了類型別名模板)。C++14 現(xiàn)在也可以創(chuàng)建變量模板。包括特化在內(nèi),通常的模板的規(guī)則都適用于變量模板的聲明和定義。
7 、聚合體成員初始化
C++11 新增 member initializer,這是一個表達式,被應(yīng)用到類作用域的成員上,如果構(gòu)造函數(shù)沒有初始化這個成員。聚合體的定義被改為明確排除任何含有 member initializer 的類,因此,他們不允許使用聚合初始化。C++14 將放松這一限制,這種類型也允許聚合初始化。如果花括號初始化列表不提供該參數(shù)的值,member initializer 會初始化它。
8 、二進制字面值
C++14 的數(shù)字可以用二進制形式指定。其格式使用前綴 0b 或 0B。這樣的語法也被 Java、Python、Perl 和 D 語言使用。
9 、數(shù)字分位符
C++14 引入單引號(’)作為數(shù)字分位符號,使得數(shù)值型的字面量可以具有更好的可讀性。Ada、D 語言、Java、Perl、Ruby 等程序設(shè)計語言使用下劃線(_)作為數(shù)字分位符號,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;三、新的標(biāo)準(zhǔn)庫特性
1 、共享的互斥體和鎖
C++14 增加了一類共享的互斥體和相應(yīng)的共享鎖。起初選擇的名字是 std::shared_mutex,但由于后來增加了與 std::timed_mutex 相似的特性,std::shared_timed_mutex 成為了更適合的名字。
2 、元函數(shù)的別名
C++11 定義了一組元函數(shù),用于查詢一個給定類型是否具有某種特征,或者轉(zhuǎn)換給定類型的某種特征,從而得到另一個類型。后一種元函數(shù)通過成員類型 type 來返回轉(zhuǎn)換后的類型,當(dāng)它們用在模板中時,必須使用 typename 關(guān)鍵字,這會增加代碼的長度。
template <class T> type_object< typename std::remove_cv< typename std::remove_reference<T>::type >::type >get_type_object(T&);利用類型別名模板,C++14 提供了更便捷的寫法。其命名規(guī)則是:如果標(biāo)準(zhǔn)庫的某個類模板(假設(shè)為 std::some_class)只含有唯一的成員,即成員類型 type,那么標(biāo)準(zhǔn)庫提供std::some_class_t作為 typename std::some_class::type 的別名。在 C++14,擁有類型別名的元函數(shù)包括: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 、關(guān)聯(lián)容器中的異構(gòu)查找
C++標(biāo)準(zhǔn)庫定義了四個關(guān)聯(lián)容器類。set 和 multiset 允許用戶根據(jù)一個值在容器中查找對應(yīng)的的同類型的值。map 和 multimap 容器允許用戶指定鍵(key)和值(value)的類型,根據(jù)鍵進行查找并返回對應(yīng)的值。然而,查找只能接受指定類型的參數(shù),在 map 和 multimap中是鍵的類型,而在 set 和 multiset 容器中就是值本身的類型。C++14 允許通過其他類型進行查找,只需要這個類型和實際的鍵類型之間可以進行比較操作。[17]這允許 std::setstd::string使用 const char*,或任何可以通過 operator< 與 std::string比較的類型作為查找的參數(shù)。為保證向后兼容性,這種異構(gòu)查找只在提供給關(guān)聯(lián)容器的比較器允許的情況下有效。標(biāo)
準(zhǔn)庫的泛型比較器,如 std::less<>與 std::greater<>允許異構(gòu)查找。
4 、標(biāo)準(zhǔn)自定義字面值
C++11 增加了自定義字面量(user-defined literals)的特性,使用戶能夠定義新的字面量后綴,但標(biāo)準(zhǔn)庫并沒有對這一特性加以利用。C++14 標(biāo)準(zhǔn)庫定義了以下字面量后綴:
? “s”,用于創(chuàng)建各種 std::basic_string 類型。
? “h”、“min”、“s”、“ms”、“us”、“ns”,用于創(chuàng)建相應(yīng)的 std::chrono::duration 時間間隔。
? 兩個"s"互不干擾,因為表示字符串的只能對字符串字面量操作,而表示秒的只針對數(shù)
字。
5 、通過類型尋址多元組
C++11 引入的 std::tuple 類型允許不同類型的值的聚合體用編譯期整型常數(shù)索引。C++14還允許使用類型代替常數(shù)索引,從多元組中獲取對象。若多元組含有多于一個這個類型的對象,將會產(chǎn)生一個編譯錯誤:
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 ambiguity6 、較小的標(biāo)準(zhǔn)庫特性
std::make_unique 可以像 std::make_shared 一樣使用,用于產(chǎn)生 std::unique_ptr 對象。
std::is_final,用于識別一個 class 類型是否禁止被繼承。
std::integral_constant 增加了一個返回常量值的 operator()。
全局 std::begin/std::end 函數(shù)之外,增加了 std::cbegin/std::cend 函數(shù),它們總是返回常量迭代器(constant iterators)。
四、已被移除或是不包含在 C++14 標(biāo)準(zhǔn)的特性
1 、關(guān)于數(shù)組的擴展
在 C++11 和之前的標(biāo)準(zhǔn)中,在堆棧上分配的數(shù)組被限制為擁有一個固定的、編譯期確定的長度。這一擴展允許在堆棧上分配的一個數(shù)組的最后一維具有運行期確定的長度。運行期確定長度的數(shù)組不可以作為對象的一部分,也不可以具有全局存儲期,他們只能被聲明為局部變量。運行期確定長度的數(shù)組也可以使用 C++11 的基于范圍的 for 循環(huán)。
同時還將添加 std::dynarray 類型,它擁有與 std::vector 和 std::array 相似的接口。代表一個固定長度的數(shù)組,其大小在運行期構(gòu)造對象時確定。std::dynarray 類被明顯地設(shè)計為當(dāng)它被放置在棧上時(直接放置在棧上,或作為另一個棧對象的成員),可以使用棧內(nèi)存而是堆內(nèi)存。
2 、Optional 值
類似于 C#中的可空類型,optional 類型可能含有或不含有一個值。這一類型基于 Boost的 boost::optional 類,而添加了 C++11 和 C++14 中的新特性,諸如移動和 in-place 構(gòu)造。它不允許用在引用類型上。這個類被專門的設(shè)計為一個 literal type(如果模板參數(shù)本身是一個literal type),因此,它在必要的情況下含有 constexpr 構(gòu)造函數(shù)。
3 、Concepts Lite
被 C++11 拒絕后,Concepts 受到徹底的修改。Concepts Lite 是 Concepts 的一個部分,僅包含類型約束,而不含 concept_map 和 axiom。它將在一個單獨的 Technical Specification中發(fā)展,并有可能加入 C++17。
總結(jié)
- 上一篇: 18.了解各种与排序有关的选择
- 下一篇: C++17特性一览