程序喵以前已经介绍过C++11的新特性和C++14的新特性,连接以下:xxx,今天向亲爱的读者们介绍下C++17的新特性,如今基本上各个编译器对C++17都已经提供完备的支持,建议你们编程中尝试使用下C++17,能够必定程度上简化代码编写,提升编程效率。c++
主要新特性以下:编程
下面程序喵一一介绍:数组
在C++17前构造一个模板类对象须要指明类型:多线程
pair<int, double> p(1, 2.2); // before c++17
C++17就不须要特殊指定,直接能够推导出类型,代码以下:app
pair p(1, 2.2); // c++17 自动推导 vector v = {1, 2, 3}; // c++17
经过结构化绑定,对于tuple、map等类型,获取相应值会方便不少,看代码:函数
std::tuple<int, double> func() { return std::tuple(1, 2.2); } int main() { auto[i, d] = func(); //是C++11的tie吗?更高级 cout << i << endl; cout << d << endl; } //========================== void f() { map<int, string> m = { {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; // 1 cout << f << endl; // 2.3f return 0; }
结构化绑定还能够改变对象的值,使用引用便可:优化
// 进化,能够经过结构化绑定改变对象的值 int main() { std::pair a(1, 2.3f); auto& [i, f] = a; i = 2; cout << a.first << endl; // 2 }
注意结构化绑定不能应用于constexprthis
constexpr auto[x, y] = std::pair(1, 2.3f); // compile error, C++20能够
结构化绑定不止能够绑定pair和tuple,还能够绑定数组和结构体等spa
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();
这里其实能够实现自定义类的结构化绑定,代码以下:线程
// 须要实现相关的tuple_size和tuple_element和get<N>方法。 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_; }; template <size_t I> auto 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<Entry> : integral_constant<size_t, 2> {}; 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 10 return 0; }
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; }
使用这种方式能够尽量约束做用域,让代码更简洁,可读性可能略有降低,可是还好
C++17前只有内联函数,如今有了内联变量,咱们印象中C++类的静态成员变量在头文件中是不能初始化的,可是有了内联变量,就能够达到此目的:
// header file struct A { static const int value; }; inline int const A::value = 10; // ==========或者======== struct A { inline static const int value = 10; }
C++17引入了折叠表达式使可变参数模板编程更方便:
template <typename ... Ts> auto sum(Ts ... ts) { return (ts + ...); } int a {sum(1, 2, 3, 4, 5)}; // 15 std::string a{"hello "}; std::string b{"world"}; cout << sum(a, b) << endl; // hello world
C++17前lambda表达式只能在运行时使用,C++17引入了constexpr lambda表达式,能够用于在编译期进行计算。
int main() { // c++17可编译 constexpr auto lamb = [] (int n) { return n * n; }; static_assert(lamb(3) == 9, "a"); }
注意:constexpr函数有以下限制:
函数体不能包含汇编语句、goto语句、label、try块、静态变量、线程局部存储、没有初始化的普通变量,不能动态分配内存,不能有new delete等,不能虚函数。
namespace A { namespace B { namespace C { void func(); } } } // c++17,更方便更温馨 namespace A::B::C { void func();) }
能够判断是否有某个头文件,代码可能会在不一样编译器下工做,不一样编译器的可用头文件有可能不一样,因此可使用此来判断:
#if defined __has_include #if __has_include(<charconv>) #define has_charconv 1 #include <charconv> #endif #endif std::optional<int> ConvertToInt(const std::string& str) { int value{}; #ifdef has_charconv const 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... 其它方式实现 #endif return std::nullopt; }
正常状况下,lambda表达式中访问类的对象成员变量须要捕获this,可是这里捕获的是this指针,指向的是对象的引用,正常状况下可能没问题,可是若是多线程状况下,函数的做用域超过了对象的做用域,对象已经被析构了,还访问了成员变量,就会有问题。
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指针,而是持有对象的拷贝,这样生命周期就与对象的生命周期不相关啦。
struct A { int a; void func() { auto f = [*this] { // 这里 cout << a << endl; }; f(); } }; int main() { A a; a.func(); return 0; }
咱们可能平时在项目中见过__declspec, attribute , #pragma指示符,使用它们来给编译器提供一些额外的信息,来产生一些优化或特定的代码,也能够给其它开发者一些提示信息。
例如:
struct A { short f[3]; } __attribute__((aligned(8))); void fatal() __attribute__((noreturn));
在C++11和C++14中有更方便的方法:
[[carries_dependency]] 让编译期跳过没必要要的内存栅栏指令 [[noreturn]] 函数不会返回 [[deprecated]] 函数将弃用的警告 [[noreturn]] void terminate() noexcept; [[deprecated("use new func instead")]] void func() {}
C++17又新增了三个:
[[fallthrough]],用在switch中提示能够直接落下去,不须要break,让编译期忽略警告
switch (i) {} case 1: xxx; // warning case 2: xxx; [[fallthrough]]; // 警告消除 case 3: xxx; break; }
使得编译器和其它开发者均可以理解开发者的意图。
[[nodiscard]] :表示修饰的内容不能被忽略,可用于修饰函数,标明返回值必定要被处理
[[nodiscard]] int func(); void F() { func(); // warning 没有处理函数返回值 }
[[maybe_unused]] :提示编译器修饰的内容可能暂时没有使用,避免产生警告
void func1() {} [[maybe_unused]] void func2() {} // 警告消除 void func3() { int x = 1; [[maybe_unused]] int y = 2; // 警告消除 }
新增from_chars函数和to_chars函数,直接看代码:
#include <charconv> 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 }
C++17增长std::variant实现相似union的功能,但却比union更高级,举个例子union里面不能有string这种类型,但std::variant却能够,还能够支持更多复杂类型,如map等,看代码:
int main() { // c++17可编译 std::variant<int, std::string> var("hello"); cout << var.index() << endl; var = 123; cout << var.index() << endl; try { var = "world"; std::string str = std::get<std::string>(var); // 经过类型获取值 var = 3; int i = std::get<0>(var); // 经过index获取对应值 cout << str << endl; cout << i << endl; } catch(...) { // xxx; } return 0; }
注意:通常状况下variant的第一个类型通常要有对应的构造函数,不然编译失败:
struct A { A(int i){} }; int main() { std::variant<A, int> var; // 编译失败 }
如何避免这种状况呢,可使用std::monostate来打个桩,模拟一个空状态。
std::variant<std::monostate, A> var; // 能够编译成功
咱们有时候可能会有需求,让函数返回一个对象,以下:
struct A {}; A func() { if (flag) return A(); else { // 异常状况下,怎么返回异常值呢,想返回个空呢 } }
有一种办法是返回对象指针,异常状况下就能够返回nullptr啦,可是这就涉及到了内存管理,也许你会使用智能指针,但这里其实有更方便的办法就是std::optional。
std::optional<int> StoI(const std::string &s) { try { return std::stoi(s); } catch(...) { return std::nullopt; } } void func() { std::string s{"123"}; std::optional<int> o = StoI(s); if (o) { cout << *o << endl; } else { cout << "error" << endl; } }
C++17引入了any能够存储任何类型的单个值,见代码:
int main() { // c++17可编译 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(); } a.reset(); if (a.has_value()) { cout << a.type().name(); } a = std::string("a"); cout << a.type().name() << " " << std::any_cast<std::string>(a) << endl; return 0; }
使用std::apply能够将tuple展开做为函数的参数传入,见代码:
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"; // error std::cout << std::apply(add_lambda, std::tuple(2.0f, 3.0f)) << '\n'; }
使用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)); }
一般咱们传递一个string时会触发对象的拷贝操做,大字符串的拷贝赋值操做会触发堆内存分配,很影响运行效率,有了string_view就能够避免拷贝操做,平时传递过程当中传递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; }
C++17使用as_const能够将左值转成const类型
std::string str = "str"; const std::string& constStr = std::as_const(str);
C++17正式将file_system归入标准中,提供了关于文件的大多数功能,基本上应有尽有,这里简单举几个例子:
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);
C++17引入了shared_mutex,能够实现读写锁,具体能够见我上一篇文章:
关于C++17的介绍就到这里,但愿对你们有所帮助~
https://en.cppreference.com/w...
https://en.cppreference.com/w...
https://en.cppreference.com/w...
https://cloud.tencent.com/dev...
https://www.jianshu.com/p/9b8...更多文章,请关注个人V X 公 主 号:程序喵大人,欢迎交流。