C++17特性一览
引言
再說回C++17,這次的更新比C++14要大不少,其中很多東西都可以為我們的C++代碼在優化代碼可讀性的同時提高效率,我們一起來看看吧
構造函數模板推導
其實我更愿意叫它模板類型自動推導,代碼展示大概就是這樣:
vector items = {1,2,3};// pair pa(4, "string"); 不直接匹配字符串pair pa(4, string("hihi"));cout << pa.second << " " << items[2] << endl;輸出:hihi 3結構化綁定
可以綁定pair,tuple,數組,結構體,結構化綁定以后也可以修改原值,也可以使自定義類型支持結構化綁定,但是要修改std,感覺沒什么必要。
std::tuple<int, double> func_two() {return std::tuple(1, 2.2); }auto[i, d] = func_two(); cout << i << " " << d << endl;map<int, string> mp = {{0, "a"},{1, "b"}, };for(const auto& [x, y] : mp){cout << x << " " << y << endl; }pair pa(4, string("hihi")); auto&[x, y] = pa; cout << x << " " << y << endl;輸出: 1 2.2 0 a 1 b 4 hihi/* vector vec = {1,2,3}; auto&[xx, yy, zz] = vec; cout << xx << endl; */ vector當然不能使用結構化綁定啦if-switch語句初始化
變量的作用域劃分的更明顯了。
if (int a = 29; a < 101) {cout << a; }聯變量
這是個非常有意思的東西,你可能會覺得函數inline可以提高效率,變量inline有什么用?這其實涉及到inline的一個一般不廣為人所知的特性,即多個翻譯單元內的重復聲明C++鏈接器只選擇一個,這意味這有inline聲明的函數和變量我們可以聲明在頭文件中了
代碼來看就是這樣:
// header file struct A {static const int value; }; inline int const A::value = 10;// ==========或者======== struct A {inline static const int value = 10; }有興趣的朋友可以看看下面兩篇文章:
《c++ inline variable 內聯變量 c++17》
《GCC,Clang 在C模式,較低優化等級下,鏈接器對內聯函數報未定義錯誤,為什么?》
折疊表達式
template <typename ... TT> constexpr auto foldSumRec(T... arg) {return (arg + ...); }C++17以前得寫成這樣:
template<typename T> auto foldSumRec (T arg) {return arg; }template<typename T1, typename... Ts> auto foldSumRec (T1 arg1, Ts... otherArgs) {return arg1 + foldSumRec(otherArgs...); }體的細節可查看這篇文章《C++17 fold expression》
constexpr lambda表達式
需要注意的是有如下約束:函數體不能包含匯編語句、goto語句、label、try塊、靜態變量、線程局部存儲、沒有初始化的普通變量,不能動態分配內存,不能有new delete等,不能使用虛函數
constexpr auto lamb = [](auto b) {int ret = 0; // C++14中放寬了lambda的標準for (size_t i = 0; i < b; i++){ret += i;}return ret;};namespace嵌套
namespace A {namespace B {namespace C {void func();}} }// c++17 namespace A::B::C {void func(); }語法糖,更方便更舒適
from_chars函數和to_chars
具體可參考《C++標準庫里自帶的數值類型和字符串互相轉換函數》
std::array<char, 3> str{"42"}; int result; std::from_chars( str.data(), str.data()+str.size(),result ); std::cout << result << std::endl;// p是填充到str以后的最后一個迭代器 if(auto [p, ec] = std::to_chars(str.data(), str.data() + str.size(), 425);ec == std::errc()){if(p == str.end()){std::cout << "hello world\n";}std::cout << std::string_view(str.data(), p - str.data()); } 輸出: 42 hello world 425std::shared_mutex
千盼萬盼你終于是來了。。這個不需要解釋了,就是讀寫鎖了。
關于與mutex的性能對比可以看這里《std::shared_mutex和std::mutex的性能對比(benchmark)》
具體內容可參考:《C++ std::shared_mutex讀寫鎖》
std::variant
具體可參考《C++17之std::variant》
struct NoDefConstr_seven{NoDefConstr_seven(int i){std::cout << "NoDefConstr::NoDefConstr(int) called\n";} }; // variant類似于union,第一個參數必須擁有默認構造函數std::variant<int, std::string> var{"hi"}; // initialized with string alternative std::cout << var.index() << std::endl; // prints 1 var = 42; // now holds int alternative std::cout << var.index() << std::endl; // prints 0 try {std::string s = std::get<std::string>(var); // access by typeint i = std::get<0>(var); // access by index } catch (const std::bad_variant_access& e) { // in case a wrong type/index is usedstd::cout << "hello\n"; }// std::variant<NoDefConstr_seven, int> v1; 第一個參數沒有構造函數 編譯失敗 // std::monostate就是防止全部的參數都沒有默認構造函數 std::variant<std::monostate, NoDefConstr_seven, int> v2;輸出: 1 0 hellostd::optional
具體可參考《C++干貨系列——C++17新特性之std::optional》
其實就是為了防止我們平時在代碼中返回一個不存在語義時隨便設置的magic number。
std::any
一般頂多variant就足夠用了,何必用Any呢 但在極端情況下,用any總比用void*強得多,鼓勵實現避免小對象的動態分配
std::any a = 1; cout << a.type().name() << " " << std::any_cast<int>(a) << endl; a = 2.2f; cout << a.type().name() << " " << std::any_cast<float>(a) << endl; if (a.has_value()) {cout << a.type().name() << std::endl; } a.reset(); // 可以這樣判斷類型 // assert(a1.type() == typeid(int)); if (a.has_value()) {cout << a.type().name() << std::endl; } a = std::string("a"); // 這個string的類型名是真的惡心 cout << a.type().name() << ": " << std::any_cast<std::string>(a) << endl;輸出: i 1 f 2.2 f NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE: a具體可查看這篇文章《C++17之std::any》
std::apply
我認為其實就是把容器的值當做函數的輸入
int add_ten(int first, int second) { return first + second; } auto add_ten_lambda = [](auto first, auto second) { return first + second; };std::cout << std::apply(add_ten, std::pair(1, 2)) << '\n'; //std::cout << add(std::pair(1, 2)) << "\n"; // error std::cout << std::apply(add_ten_lambda, std::tuple(2.2f, 3.0f)) << '\n';輸出: 3 5.2具體可參考《使用std :: apply應用可變參數函數(Applying a variadic function with std::apply)》
std::make_from_tuple
使用make_from_tuple可以將tuple展開作為構造函數參數:
struct Foo {Foo(int first, float second, int third) {std::cout << first << ", " << second << ", " << third << "\n";} }; int main() {auto tuple = std::make_tuple(42, 3.14f, 0);std::make_from_tuple<Foo>(std::move(tuple)); }std::string_view
平時代碼中可以大規模使用的一個特性。其實對于string的爭論一直沒有停止過,很多人認為string是字節串而不是字符串,因為string是可以改變的,這一切爭論到C++17可以停止了。string_view的substr與構造時間復雜度為O(1),且不會產生拷貝,因為substr只是一個指針操作。
可以參考如下兩篇文章:
《C++17,使用 string_view 來避免復制》
《C++17 string_view的高效以及陷阱》
std::file_system
具體可參考《c++ filesystem》
這其實模子是boost的file_system,最早2003年就出來了,因為是跨平臺的,所以可以說是非常舒服了。
有一說一,在我的機子上跑不了這個。
代碼:
namespace fs = std::filesystem; fs::path pathToShow("/home/lzl/Desktop/execise"); cout << "exists() = " << fs::exists(pathToShow) << "\n" << "root_name() = " << pathToShow.root_name() << "\n" << "root_path() = " << pathToShow.root_path() << "\n";跑完以后顯示這樣:
./a.out: /lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.26' not found (required by ./a.out)并行算法庫
這可以說是C++17最重要的幾個特性之一,這個特性為幾乎所有標準庫函數加上一個執行策略參數,可以讓使用者選擇并行還是串行,這不僅包括七個新的算法,還有我們熟知的sort等。
具體可參考:
《[譯]C++17,標準庫新引入的并行算法?》
《STL并行算法庫》
constexpr if
在C++17以前泛型函數中是不可以出現if這樣的邏輯判斷符的
template <int N, int... Ns> auto sum() {if (sizeof...(Ns) == 0) // 若參數包為空, 直接返回 Nreturn N;else // 否則進行遞歸調用return N + sum<Ns...>(); }這樣就會編譯失敗。
以前的做法是用模板遞歸,就像這樣:
// 只有一個模板參數時調用此模板 template<int N> int sum() {return N; }// 模板參數 > 2 個時調用此模板 template <int N, int N2, int... Ns> int sum() {return N + sum<N2, Ns...>(); }現在我們可以這樣:
template <int N, int... Ns> auto sum() {if constexpr (0 == sizeof...(Ns))return N;elsereturn N + sum<Ns...>(); }具體可參考:《C++17 之 “constexpr if”》
總結
- 上一篇: C++14 新特性
- 下一篇: C++二维数组按行遍历和按列遍历的区别