当前位置:首页 > 公众号精选 > 程序喵大人
[导读]程序喵之前已经介绍过C++11的新特性和C++14的新特性(点击对应文字,直接访问),今天向亲爱的读者们介绍下C++17的新特性,现在基本上各个编译器对C++17都已经提供完备的支持,建议大家编程中尝试使用下C++17,可以一定程度上简化代码编写,提高编程效率。 主


程序喵之前已经介绍过C++11的新特性" tab="innerlink" data-linktype="2" rel="nofollow">C++11的新特性和C++14的新特性(点击对应文字,直接访问),今天向亲爱的读者们介绍下C++17的新特性,现在基本上各个编译器对C++17都已经提供完备的支持,建议大家编程中尝试使用下C++17,可以一定程度上简化代码编写,提高编程效率。

主要新特性如下:

  • 构造函数模板推导

  • 结构化绑定

  • if-switch语句初始化

  • 内联变量

  • 折叠表达式

  • constexpr lambda表达式

  • namespace嵌套

  • __has_include预处理表达式

  • 在lambda表达式用*this捕获对象副本

  • 新增Attribute

  • 字符串转换

  • std::variant

  • std::optional

  • std::any

  • std::apply

  • std::make_from_tuple

  • as_const

  • std::string_view

  • file_system

  • std::shared_mutex

下面,程序喵一一介绍:

构造函数模板推导

在C++17前构造一个模板类对象需要指明类型:

pair<int, double> p(1, 2.2); // before c++17

C++17就不需要特殊指定,直接可以推导出类型,代码如下:

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}

注意结构化绑定不能应用于constexpr

constexpr auto[x, y] = std::pair(1, 2.3f); // compile error, C++20可以

结构化绑定不止可以绑定pair和tuple,还可以绑定数组和结构体等。

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;}

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;}

使用这种方式可以尽可能约束作用域,让代码更简洁,但是可读性略有下降。

内联变量

C++17前只有内联函数,现在有了内联变量,我们印象中C++类的静态成员变量在头文件中是不能初始化的,但是有了内联变量,就可以达到此目的:

// header filestruct 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)}; // 15std::string a{"hello "};std::string b{"world"};cout << sum(a, b) << endl; // hello world

constexpr lambda表达式

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嵌套

namespace A { namespace B { namespace C { void func(); } }}
// c++17,更方便更舒适namespace A::B::C { void func();)}

__has_include预处理表达式

可以判断是否有某个头文件,代码可能会在不同编译器下工作,不同编译器的可用头文件有可能不同,所以可以使用此来判断:

#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捕获对象副本

正常情况下,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;}

新增Attribute

我们可能平时在项目中见过__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}

std::variant

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; // 可以编译成功

std::optional

我们有时候可能会有需求,让函数返回一个对象,如下:

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; }}

std::any

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

使用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';}

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_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;}

as_const

C++17使用as_const可以将左值转成const类型

std::string str = "str";const std::string& constStr = std::as_const(str);

file_system

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);

std::shared_mutex

C++17引入了shared_mutex,可以实现读写锁,具体可以见我上一篇文章:C++14新特性的所有知识点全在这儿啦!

关于C++17的介绍就到这里,希望对大家有所帮助~

参考资料

https://en.cppreference.com/w/cpp/utility/make_from_tuple
https://en.cppreference.com/w/cpp/utility/apply
https://en.cppreference.com/w/cpp/17
https://cloud.tencent.com/developer/article/1383177
https://www.jianshu.com/p/9b8eeddbf1e4


C++14新特性的所有知识点全在这儿啦!

面试系列之C++的对象布局【建议收藏】


c++11新特性,所有知识点都在这了!

你的c++团队还在禁用异常处理吗?

JNI编程如何巧妙获取JNIEnv

Linux 为什么要动态链接?与静态链接的区别是什么?

内存对齐之格式修订版

c++11新特性之智能指针



免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭