当前位置: 首页 > news >正文

C++17常用新特性

目录

一、结构化绑定

1.绑定到数组

2.绑定到结构体/类

3.与范围for结合遍历关联式容器

二、if和switch中的初始化语句

1.if初始化语句用法

2.switch初始化语句用法

三、if constexpr

1.普通函数中使用

2.模板函数中使用

四、内联变量

五、类模板参数推导

六、编译期Lambda

七、std::optional

八、std::variant 和std::visit

1.使用普通函数对象(仿函数)

2.使用Lambda表示式(C++17支持)

std::decay_t

九、std::string_view


 

一、结构化绑定

允许将一个复合类型(数组、结构体/类、元组等)直接解包绑定到多个变量,极大的简化代码,更加高效。

1.绑定到数组

int main()
{int arr[3] = { 1,2,3 };auto& [a, b, c] = arr;cout << a; //1return 0;
}

2.绑定到结构体/类

只有结构体/类中的共有数据成员才可以被绑定。

绑定到std::pair和std::tuple

int main()
{//处理像std::map::insert返回的pair时,结构化绑定很有用map<int, string> myMap;const auto& [iter, success] = myMap.insert({ 1,"blue" });if (success) {cout << "Insertion successful!" << endl;}//绑定到std::tupletuple<int, float, string> tup(61, 1.1, "blue");auto [num, fl, msg] = tup;cout << num << "," << fl << "," << msg << endl;return 0;
}

3.与范围for结合遍历关联式容器

	//C++17结构化绑定,清晰明了map<int, string> myMap = { {1,"blue"},{2,"blue"} };for (const auto& [key, value] : myMap) {cout << "Key: " << key << ", Value: " << value << endl;}

二、if和switch中的初始化语句

允许在if/switch的条件判断之前,定义并初始化一个局部变量,该变量的作用域仅限于这个if/switch语句块内。

1.if初始化语句用法

if (init-statement; condition) {// 如果 condition 为 true,执行这里的代码
} else {  // 可选的 else// 如果 condition 为 false,执行这里的代码
}
	vector<int> v = { 1,2,3,4,5,6 };int target = 3;if (auto iter = std::find(v.begin(), v.end(), target);iter != v.end()) {cout << "Found." << *iter << endl;}else {cout << "Not found." << endl;}

2.switch初始化语句用法

switch (init-statement; expression) {case value1:// ...break;case value2:// ...break;default:// ...
}
	map<int, string> colers = { {1,"Blue"},{2,"Green"} ,{3,"Write"} };int colerId = 1;switch (auto it = colers.find(colerId);it != colers.end() ? it->second[0] : ' ') {case 'B':cout << "Found: " << it->second << endl;break;case 'G':cout << "Found: " << it->second << endl;break;default:cout << "Not found." << endl;}

三、if constexpr

编译期if是一种在编译时进行条件判断的机制,它允许在模板代码中根据常量表达式来选择不同的代码路径,不会编译被剔除的代码路径,从而避免一些语法或类型错误。

如果使用的是普通的if,那么编译器会尝试编译所有分支,可能导致类型不匹配的错误。

1.普通函数中使用

#include <iostream>void check(int x) {if constexpr (sizeof(int) == 4) {  // 编译期常量std::cout << "int is 4 bytes on this platform\n";} else {std::cout << "int is not 4 bytes\n";}
}int main() {check(10);  // 输出:int is 4 bytes on this platform(在大多数平台上)
}

2.模板函数中使用

template <typename T>
void printTypeInfo(const T& value)
{if constexpr (std::is_integral_v<T>) {cout << "Integral type: " << value << endl;}else if constexpr (std::is_floating_point_v<T>) {cout << "Floating point type: " << value << endl;}else if constexpr (std::is_same_v<T,std::string>) {cout << "String type: " << value << endl;}else {cout << "Unknow type" << endl;}
}

编译期常量布尔表达式

C++17标准库<type_traits>中提供的类型特性工具,用于在编译期查询某个类型T的属性。

  • std::is_integral_v<T>:判断类型T是不是整型,比如:int、short、long、unsigned、bool等(会在编译期判断类型T是不是整型类型返回true或false)。
  • std::is_floating_point_v<T>:判断类型T是不是浮点型,比如:float、double、long double。
  • std::is_same_v<T, U>:判断类型T和U是否是完全相同的类型。

四、内联变量

允许在头文件中定义(而不仅仅是声明)一个变量,并且保证这个变量在整个程序中只有一个定义,避免了在多个翻译单元中包含同一个同文件时导致的 "重复定义" 链接错误。

	//test.h(C++17之前错误的写法)const string str = "blue";
  • 如果a.cpp和b.cpp都包含了test.h头文件,那么在链接时,str这个全局变量会有两个定义(一个在a.obj,一个在b.obj),导致链器报错。
  • 传统的解决方法(C++17之前):使用extern关键字在test.h中声明变量,然后在一个仅且一个.cpp文件中定义这个变量,但这比较麻烦。

使用inline关键字:

	//test.h(C++17的正确写法)inline string str = "blue";
  • 在多个源文件中包含test.h时,链接器会知道这些str变量都指向同一个实体,不会发生重复定义的错误。

五、类模板参数推导

不需要制定模板参数,编译器会根据构造函数参数自动推导。简化模板类实例化,特别是vector、map、pair、tuple等类型。

	std::pair p(1, 3.14); //推导为 std::pair<int,double>std::tuple t(1, 3.14, "blue"); //推导为 std::tuple<int,double,const char*> std::vector v = { 1,2,3 }; //推到为 std::vector<int>

六、编译期Lambda

C++17允许Lambda表达式在constexpr上下文中使用,Lambda表达式本身也可以是constexpr,提升运行时效率。

constexpr int calculator()
{auto lam = [](int x) {return x * x;};return lam(3);
}constexpr auto lam = [](int x) {return 2 * x;};

七、std::optional<T>

optional<T>是C++17引入的一个类模板,用于表示一个可能有值,也可能没有值(即”空“)的对象,它的主要目的是用来替代传统通过一些方式,比如:使用nullptr、-1或bool类型的变量,来表示某个值可能有或没有的情况,这种传统的方式是不安全也不太优雅。

C++17中的optional<T>更安全,更现代,不需要依赖特殊值nullptr、-1等避免误用这些特殊值。

struct User
{string name;int age;
};std::optional<User> find_user_by_id(const int& id)
{if (id == 99) {return User{ "张三",20 };}else {return nullopt;}
}int main()
{User user{ "张三",20 };auto o = find_user_by_id(55);if (o.has_value()) {cout << "name: " << o->name << " age: " << o->age << endl;}else {cout << "Not found." << endl;}return 0;
}
  • 判断std::optional<T>类型对象o是否有值:if(o.has_value)或if(o)。
  • 访问内部数据:o.value()(无值会抛出std::bad_optional_access异常)或o.value_or(def)(较安全,无值返回设置的默认值def)。
  • std::nullopt:表示空值。
  • std::reset():清空o中的值。
  • std::emplace(args...):重新构造o中的新值。
  • *o/o->:访问o中的值。

八、std::variant<T...>和std::visit

C++17中的variant是安全类型的联合体,相较于union具有类型安全、现代、功能丰富的优点。

使用传统的union存储多个数据时,需要我们自己跟踪记录当前存的是哪个类型的数据,防止错误的类型会导致未定义行为,variant配合visit则解决了这个问题。

使用std::variant时需要包含头文件<variant>。

	std::variant<int, string> var;var = string("hhh");try {cout << std::get<string>(var) << endl;}catch (const bad_variant_access& exception) {cout << exception.what() << endl;}
  • 使用std::get<T>获取variant对象当前存储的数据时,该数据类型必须和T的类型一致,负责会抛出std::bad_variant_access异常。

std::visit(visitor,var)传入一个访问者visitor,它会根据variant当前存储的实际类型,调用对应的处理函数。

1.使用普通函数对象(仿函数)

using MyVariant = std::variant<int, double, string>;
class MyVisitor
{
public:void operator()(int i) const{cout << "Is int: " << i << endl;}void operator()(const string& s) const{cout << "Is string" << s << endl;}
};int main()
{MyVariant v1 = 18;MyVariant v2 = string("blue");MyVisitor visitor;std::visit(visitor, v1);std::visit(visitor, v2);reuturn 0;
}

2.使用Lambda表示式(C++17支持)

using MyVariant = std::variant<int, double, string>;
int main()
{MyVariant var = 8888;std::visit([](auto&& arg) {using T = std::decay_t<decltype(arg)>;if constexpr (std::is_same_v<T, int>) {cout << "Is int: " << arg << endl;}else if constexpr (std::is_same_v<T, string>) {cout << "Is string: " << arg << endl;}}, var);return 0;
}
std::decay_t<T>
  • std::decay_t<T>是C++11中的类型转换工具,定义在<type_traits>中,他是std::decay<T>::type的简写形式(C++14开始支持_t后缀的快捷方式)。
  • std::decay_t<T>主要对类型做了:移除引用、移除const,volatile属性、将数组和函数类型转换为对应的指针类型。

九、std::string_view

string_view是非拥有式字符串视图,提供对字符串数据的只读访问,使用时需包含头文件#include <string_view>

关键点:

  • 非拥有式:string_view对象部管理内存,只保存指向字符串的指针和字符串的大小。
  • 性能优异:避免了不必要的字符串拷贝和内存分配(底层只是复制指针和大小)。
  • 只读属性:不能通过string_view对象修改底层数据。
  • 生命周期:必须确保底层数据的生命周期长于string_view对象,防止悬空引用。

string_view几个常见的陷阱:

string_view create_dangerous_view()
{string str("temporary str");string_view dangerous_view = str;return dangerous_view;//Error! str底层管理的数据空间已销毁//dangerous_view仍然指向被销毁的无效空间
}
void dangerous_lifetime()
{string_view dangerous_view;{string str = "temporary str";dangerous_view = str;cout << "在作用域内:" << dangerous_view << endl;}//Error 未定义行为!cout << "在作用域外:" << dangerous_view << endl;return;
}
http://www.dtcms.com/a/600647.html

相关文章:

  • oj题 ——— 链式二叉树oj题
  • 数据库项目实战五
  • Python调用Java接口失败(Java日志打印警告:JSON parse error:xxxx)
  • 没有网站如何做SEO推广有用吗怎么不花钱自己开网店
  • ArkTS分布式设计模式浅析
  • 倍福PGV100-F200A-R4-V19使用手册
  • FD2000/4的UEFI编译和烧录文件打包过程记录
  • 微信小程序map自定义气泡customCallout
  • 如何在ubuntu调用exe文件
  • Polar MISC (中)
  • 《理解数据在内存中的存储 --- 解密数据在计算机底层的存储秘密》
  • 兰州网站建设公网站可以换虚拟主机吗
  • 营销型网站建设评价深圳福田住房和建设局网站官网
  • 遍历访问阿里云节点下的所有文件信息并写入excel文件
  • 平台消息推送(go)
  • uniapp集成爱山东获取用户信息
  • Python编程实战 - Python实用工具与库 - 操作Excel:openpyxl / pandas
  • 开展我国电子网站建设wordpress表白
  • Java 在 Excel 中添加或删除批注:Spire.XLS for Java 实践指南
  • uniapp 使用unocss的问题
  • [Linux——Lesson23.线程概念与控制:线程基础]
  • 四大主流浏览器Chrome、Edge、Safari、Firefox内核检测免费工具评测
  • 弱网通话没保障?多网聚合,逐包调度,新技术扫除网络痛点
  • 网站制作公司的网站贵阳网站改版
  • 电脑硬件价格呈现持续上涨趋势及软件优化的必要性
  • Spring集成kafka的最佳方式
  • 设计网站怎么做网业是什么行业
  • RK3588应用分享之国产化系统-开源鸿蒙OpenHarmony
  • RabbitMQ-基础-总结
  • 学习react第二天