|
程序喵之前已經(jīng)介紹過C++11的新特性和C++14的新特性(點(diǎn)擊對(duì)應(yīng)文字,直接訪問),今天向親愛的讀者們介紹下C++17的新特性,現(xiàn)在基本上各個(gè)編譯器對(duì)C++17都已經(jīng)提供完備的支持,建議大家編程中嘗試使用下C++17,可以一定程度上簡化代碼編寫,提高編程效率。 主要新特性如下: 構(gòu)造函數(shù)模板推導(dǎo) 結(jié)構(gòu)化綁定 if-switch語句初始化 內(nèi)聯(lián)變量 折疊表達(dá)式 constexpr lambda表達(dá)式 namespace嵌套 __has_include預(yù)處理表達(dá)式 在lambda表達(dá)式用*this捕獲對(duì)象副本 新增Attribute 字符串轉(zhuǎn)換 std::variant std::optional std::any std::apply std::make_from_tuple as_const std::string_view file_system std::shared_mutex 下面,程序喵一一介紹: 構(gòu)造函數(shù)模板推導(dǎo) 在C++17前構(gòu)造一個(gè)模板類對(duì)象需要指明類型: pairp(1, 2.2); // before c++17 C++17就不需要特殊指定,直接可以推導(dǎo)出類型,代碼如下: pair p(1, 2.2); // c++17 自動(dòng)推導(dǎo)vector v = {1, 2, 3}; // c++17 結(jié)構(gòu)化綁定 通過結(jié)構(gòu)化綁定,對(duì)于tuple、map等類型,獲取相應(yīng)值會(huì)方便很多,看代碼: std::tuplefunc() {return std::tuple(1, 2.2);} int main() {auto[i, d] = func(); //是C++11的tie嗎?更高級(jí)cout << i << endl;cout << d << endl;} //==========================void f() {mapm = {{0, 'a'},{1, 'b'}, };for (const auto &[i, s] : m) {cout << i << ' ' << s << endl;}} // ====================int main() {std::pair a(1, 2.3f);auto[i, f] = a;cout << i << endl; // 1cout << f << endl; // 2.3freturn 0;} 結(jié)構(gòu)化綁定還可以改變對(duì)象的值,使用引用即可: // 進(jìn)化,可以通過結(jié)構(gòu)化綁定改變對(duì)象的值int main() { std::pair a(1, 2.3f); auto& [i, f] = a; i = 2; cout << a.first << endl; // 2} 注意結(jié)構(gòu)化綁定不能應(yīng)用于constexpr constexpr auto[x, y] = std::pair(1, 2.3f); // compile error, C++20可以 結(jié)構(gòu)化綁定不止可以綁定pair和tuple,還可以綁定數(shù)組和結(jié)構(gòu)體等。 int array[3] = {1, 2, 3};auto [a, b, c] = array;cout << a << ' ' << b << ' ' << c << endl; // 注意這里的struct的成員一定要是public的struct Point {int x;int y;};Point func() {return {1, 2};}const auto [x, y] = func(); 這里其實(shí)可以實(shí)現(xiàn)自定義類的結(jié)構(gòu)化綁定,代碼如下: // 需要實(shí)現(xiàn)相關(guān)的tuple_size和tuple_element和get方法。class Entry {public:void Init() {name_ = 'name';age_ = 10;} std::string GetName() const { return name_; }int GetAge() const { return age_; }private:std::string name_;int age_;}; templateauto get(const Entry& e) {if constexpr (I == 0) return e.GetName();else if constexpr (I == 1) return e.GetAge();} namespace std {template<> struct tuple_size: integral_constant{};template<> struct tuple_element<0, Entry> { using type = std::string; };template<> struct tuple_element<1, Entry> { using type = int; };} int main() {Entry e;e.Init();auto [name, age] = e;cout << name << ' ' << age << endl; // name 10return 0;} if-switch語句初始化 C++17前if語句需要這樣寫代碼: int a = GetValue();if (a < 101) { cout << a;} C++17之后可以這樣: // if (init; condition) if (int a = GetValue()); a < 101) {cout << a;} string str = 'Hi World';if (auto [pos, size] = pair(str.find('Hi'), str.size()); pos != string::npos) {std::cout << pos << ' Hello, size is ' << size;} 使用這種方式可以盡可能約束作用域,讓代碼更簡潔,但是可讀性略有下降。 內(nèi)聯(lián)變量 C++17前只有內(nèi)聯(lián)函數(shù),現(xiàn)在有了內(nèi)聯(lián)變量,我們印象中C++類的靜態(tài)成員變量在頭文件中是不能初始化的,但是有了內(nèi)聯(lián)變量,就可以達(dá)到此目的: // header filestruct A {static const int value; };inline int const A::value = 10; // ==========或者========struct A {inline static const int value = 10;} 折疊表達(dá)式 C++17引入了折疊表達(dá)式使可變參數(shù)模板編程更方便: templateauto sum(Ts ... ts) {return (ts + ...);}int a {sum(1, 2, 3, 4, 5)}; // 15std::string a{'hello '};std::string b{'world'};cout << sum(a, b) << endl; // hello world constexpr lambda表達(dá)式 C++17前l(fā)ambda表達(dá)式只能在運(yùn)行時(shí)使用,C++17引入了constexpr lambda表達(dá)式,可以用于在編譯期進(jìn)行計(jì)算。 int main() { // c++17可編譯 constexpr auto lamb = [] (int n) { return n * n; }; static_assert(lamb(3) == 9, 'a');} 注意 constexpr函數(shù)有如下限制: 函數(shù)體不能包含匯編語句、goto語句、label、try塊、靜態(tài)變量、線程局部存儲(chǔ)、沒有初始化的普通變量,不能動(dòng)態(tài)分配內(nèi)存,不能有new delete等,不能虛函數(shù)。 namespace嵌套 namespace A {namespace B {namespace C {void func();}}} // c++17,更方便更舒適namespace A::B::C {void func();)} __has_include預(yù)處理表達(dá)式 可以判斷是否有某個(gè)頭文件,代碼可能會(huì)在不同編譯器下工作,不同編譯器的可用頭文件有可能不同,所以可以使用此來判斷: #if defined __has_include#if __has_include()#define has_charconv 1#include#endif#endif std::optionalConvertToInt(const std::string& str) {int value{};#ifdef has_charconvconst auto last = str.data() + str.size();const auto res = std::from_chars(str.data(), last, value);if (res.ec == std::errc{} && res.ptr == last) return value;#else// alternative implementation...其它方式實(shí)現(xiàn)#endifreturn std::nullopt;} 在lambda表達(dá)式用*this捕獲對(duì)象副本 正常情況下,lambda表達(dá)式中訪問類的對(duì)象成員變量需要捕獲this,但是這里捕獲的是this指針,指向的是對(duì)象的引用,正常情況下可能沒問題,但是如果多線程情況下,函數(shù)的作用域超過了對(duì)象的作用域,對(duì)象已經(jīng)被析構(gòu)了,還訪問了成員變量,就會(huì)有問題。 struct A {int a;void func() {auto f = [this] {cout << a << endl;};f();} };int main() {A a;a.func();return 0;} 所以C++17增加了新特性,捕獲*this,不持有this指針,而是持有對(duì)象的拷貝,這樣生命周期就與對(duì)象的生命周期不相關(guān)啦。 struct A { int a; void func() { auto f = [*this] { // 這里 cout << a << endl; }; f(); } };int main() { A a; a.func(); return 0;} 新增Attribute 我們可能平時(shí)在項(xiàng)目中見過__declspec__, __attribute__ , #pragma指示符,使用它們來給編譯器提供一些額外的信息,來產(chǎn)生一些優(yōu)化或特定的代碼,也可以給其它開發(fā)者一些提示信息。 例如: struct A { short f[3]; } __attribute__((aligned(8))); void fatal() __attribute__((noreturn)); 在C++11和C++14中有更方便的方法: [[carries_dependency]] 讓編譯期跳過不必要的內(nèi)存柵欄指令[[noreturn]] 函數(shù)不會(huì)返回[[deprecated]] 函數(shù)將棄用的警告 [[noreturn]] void terminate() noexcept;[[deprecated('use new func instead')]] void func() {} C++17又新增了三個(gè): [[fallthrough]]:用在switch中提示可以直接落下去,不需要break,讓編譯期忽略警告 switch (i) {}case 1:xxx; // warningcase 2:xxx;[[fallthrough]]; // 警告消除case 3:xxx;break;} 使得編譯器和其它開發(fā)者都可以理解開發(fā)者的意圖。 [[nodiscard]] :表示修飾的內(nèi)容不能被忽略,可用于修飾函數(shù),標(biāo)明返回值一定要被處理 [[nodiscard]] int func();void F() { func(); // warning 沒有處理函數(shù)返回值} [[maybe_unused]] :提示編譯器修飾的內(nèi)容可能暫時(shí)沒有使用,避免產(chǎn)生警告 void func1() {}[[maybe_unused]] void func2() {} // 警告消除void func3() {int x = 1;[[maybe_unused]] int y = 2; // 警告消除} 字符串轉(zhuǎn)換 新增from_chars函數(shù)和to_chars函數(shù),直接看代碼: #include int main() {const std::string str{'123456098'};int value = 0;const auto res = std::from_chars(str.data(), str.data() + 4, value);if (res.ec == std::errc()) {cout << value << ', distance ' << res.ptr - str.data() << endl;} else if (res.ec == std::errc::invalid_argument) {cout << 'invalid' << endl;}str = std::string('12.34);double val = 0;const auto format = std::chars_format::general;res = std::from_chars(str.data(), str.data() + str.size(), value, format); str = std::string('xxxxxxxx');const int v = 1234;res = std::to_chars(str.data(), str.data() + str.size(), v);cout << str << ', filled ' << res.ptr - str.data() << ' characters \n';// 1234xxxx, filled 4 characters} std::variant C++17增加std::variant實(shí)現(xiàn)類似union的功能,但卻比union更高級(jí),舉個(gè)例子union里面不能有string這種類型,但std::variant卻可以,還可以支持更多復(fù)雜類型,如map等,看代碼: int main() { // c++17可編譯std::variantvar('hello');cout << var.index() << endl;var = 123;cout << var.index() << endl; try {var = 'world';std::string str = std::get(var); // 通過類型獲取值var = 3;int i = std::get<0>(var); // 通過index獲取對(duì)應(yīng)值cout << str << endl;cout << i << endl;} catch(...) {// xxx;}return 0;} 注意 一般情況下variant的第一個(gè)類型一般要有對(duì)應(yīng)的構(gòu)造函數(shù),否則編譯失?。?/p> struct A { A(int i){}};int main() { std::variant var; // 編譯失敗} 如何避免這種情況呢,可以使用std::monostate來打個(gè)樁,模擬一個(gè)空狀態(tài)。 std::variantvar; // 可以編譯成功 std::optional 我們有時(shí)候可能會(huì)有需求,讓函數(shù)返回一個(gè)對(duì)象,如下: struct A {};A func() { if (flag) return A(); else { // 異常情況下,怎么返回異常值呢,想返回個(gè)空呢 }} 有一種辦法是返回對(duì)象指針,異常情況下就可以返回nullptr啦,但是這就涉及到了內(nèi)存管理,也許你會(huì)使用智能指針,但這里其實(shí)有更方便的辦法就是std::optional。 std::optionalStoI(const std::string &s) {try {return std::stoi(s);} catch(...) {return std::nullopt;}} void func() {std::string s{'123'};std::optionalo = StoI(s);if (o) {cout << *o << endl;} else {cout << 'error' << endl;}} std::any C++17引入了any可以存儲(chǔ)任何類型的單個(gè)值,見代碼: int main() { // c++17可編譯 std::any a = 1; cout << a.type().name() << ' ' << std::any_cast(a) << endl; a = 2.2f; cout << a.type().name() << ' ' << std::any_cast(a) << endl; if (a.has_value()) { cout << a.type().name(); } a.reset(); if (a.has_value()) { cout << a.type().name(); } a = std::string('a'); cout << a.type().name() << ' ' << std::any_cast(a) << endl; return 0;} std::apply 使用std::apply可以將tuple展開作為函數(shù)的參數(shù)傳入,見代碼: int add(int first, int second) { return first + second; } auto add_lambda = [](auto first, auto second) { return first + second; }; int main() {std::cout << std::apply(add, std::pair(1, 2)) << '\n';std::cout << add(std::pair(1, 2)) << '\n'; // errorstd::cout << std::apply(add_lambda, std::tuple(2.0f, 3.0f)) << '\n';} std::make_from_tuple 使用make_from_tuple可以將tuple展開作為構(gòu)造函數(shù)參數(shù) 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(std::move(tuple));} std::string_view 通常我們傳遞一個(gè)string時(shí)會(huì)觸發(fā)對(duì)象的拷貝操作,大字符串的拷貝賦值操作會(huì)觸發(fā)堆內(nèi)存分配,很影響運(yùn)行效率,有了string_view就可以避免拷貝操作,平時(shí)傳遞過程中傳遞string_view即可。 void func(std::string_view stv) { cout << stv << endl; } int main(void) {std::string str = 'Hello World';std::cout << str << std::endl; std::string_view stv(str.c_str(), str.size());cout << stv << endl;func(stv);return 0;} as_const C++17使用as_const可以將左值轉(zhuǎn)成const類型 std::string str = 'str';const std::string& constStr = std::as_const(str); file_system C++17正式將file_system納入標(biāo)準(zhǔn)中,提供了關(guān)于文件的大多數(shù)功能,基本上應(yīng)有盡有,這里簡單舉幾個(gè)例子: namespace fs = std::filesystem;fs::create_directory(dir_path);fs::copy_file(src, dst, fs::copy_options::skip_existing);fs::exists(filename);fs::current_path(err_code); std::shared_mutex C++17引入了shared_mutex,可以實(shí)現(xiàn)讀寫鎖,具體可以見我上一篇文章:C++14新特性的所有知識(shí)點(diǎn)全在這兒啦! 關(guān)于C++17的介紹就到這里,希望對(duì)大家有所幫助~ 參考資料https://en./w/cpp/utility/make_from_tuple https://en./w/cpp/utility/apply https://en./w/cpp/17 https://cloud.tencent.com/developer/article/1383177 https://www.jianshu.com/p/9b8eeddbf1e4 |
|
|