news 2026/7/14 6:34:50

C++17 主要新特性 汇总

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++17 主要新特性 汇总

前言:大家知道自从C++11之后,C++编程发生了"革命性"的变化,C++11引入了很多高级特性,如 智能指针、右值引用、移动语义、Lambda表达式等等。目前很多大厂C++项目采用的更多可能是C++17版本,C++17有哪些主要的新特性呢?

目录

一、语法增强

1.1 结构化绑定

1.2 带初始化的if/switch

1.3 类模板参数推导

1.4 内联变量(inline)

1.5 折叠表达式

二、标准库新增类型

2.1 std::optional

2.2 std::varaint

2.3 std::any

2.4 std::string_view

三、编译期增强


一、语法增强

1.1 结构化绑定

允许直接将多元组(如std::tuple,std::pair)、数组或公开数据成员的结构体,Unpack(解包)到独立的变量中。

编译器在底层会自动创建隐藏的对象,并将你声明的变量名绑定到该对象的子对象上。

例如:

#include <iostream> #include <map> #include <tuple> // 返回多个值的函数 std::tuple<int, double, std::string> get_data() { return {1, 3.14, "hello"}; } int main() { // 1. 解包 tuple auto [id, value, name] = get_data(); std::cout << name << std::endl; // 2. 方便地遍历 map std::map<int, std::string> m = {{1, "A"}, {2, "B"}}; for (const auto& [key, val] : m) { // C++17以前需要 it.first, it.second std::cout << key << ": " << val << std::endl; } }

1.2 带初始化的if/switch

该机制类似于for循环中的初始化部分。允许在ifswitch语句的条件判断之前声明并初始化变量。这样做的好处是,该变量的作用域被严格限制在if块及其对应的else块内,避免了命名空间的污染。

例如:

if (auto it = m.find(12); it != m.end()) { ... } // 等价于: { auto it = m.find(12); if (it != m.end()) { ... } }

1.3 类模板参数推导

在 C++17 之前,实例化类模板时必须显式指定类型(如std::pair<int, double>)。C++17之后,编译器可通过构造函数的参数自动推导类模板的参数类型。

例如:

#include <vector> #include <utility> #include <mutex> int main() { // C++17 之前 std::pair<int, double> p1(1, 2.0); std::vector<int> v1 = {1, 2, 3}; std::lock_guard<std::mutex> lock1(my_mutex); // C++17 之后:直接省略尖括号 std::pair p2(1, 2.0); // 推导为 std::pair<int, double> std::vector v2 = {1, 2, 3}; // 推导为 std::vector<int> std::lock_guard lock2(my_mutex); // 推导为 std::lock_guard<std::mutex> }

1.4 内联变量(inline)

C++17以前,在头文件中定义类的静态成员变量会违反 ODR(单一定义规则),导致链接错误。通常需要在.cpp文件中进行初始化。C++17 引入了inline变量,允许多次定义同一个变量(通常在头文件中),链接器会自动将它们合并为同一个实例。

例如:

// myheader.hpp inline int global = 0; // 所有包含此头文件的 .cpp 共享同一个 global变量

1.5 折叠表达式

该机制极大地简化了可变参数模板的处理,例如:实现一个接受任意数量参数的加法函数

#include <iostream> // C++17 之前的做法:需要定义递归终止条件和展开逻辑(非常繁琐) // C++17 做法:折叠表达式 template<typename... Args> auto sum(Args... args) { // (args + ...) 会被展开为 args1 + (args2 + (args3 + ...)) return (args + ...); } int main() { std::cout << sum(1, 2, 3.5, 4) << std::endl; // 输出 10.5 }

二、标准库新增类型

2.1 std::optional

用来表示一个对象“可能包含值,也可能不包含值”,内部通过一个布尔标志位加对象本身来实现。

.has_value():返回布尔标志;.value():返回对象中的值。

例如:

#include <optional> #include <iostream> std::optional<int> find_even(const std::vector<int>& v) { for (int num : v) { if (num % 2 == 0) return num; // 找到了,返回包含值的 optional } return std::nullopt; // 没找到,返回空 optional } int main() { auto result = find_even({1, 3, 5, 6, 7}); if (result.has_value()) { std::cout << "Found: " << result.value() << "\n"; } }

2.2 std::varaint

它是类型安全的联合体,与传统union不同,它不仅能安全地处理复杂类型(如std::string),还能知道当前到底存储的是哪种类型。

例如:

#include <variant> #include <string> #include <iostream> int main() { // v 可以是 int, double 或 string std::variant<int, double, std::string> v; v = 9; std::cout << std::get<int>(v) << "\n"; v = "Hello"; // 推荐使用 std::visit 进行模式匹配(根据当前类型执行不同逻辑) std::visit([](auto&& arg) { std::cout << "Current value is: " << arg << "\n"; }, v); }

2.3 std::any

它是类型安全的void*,可以容纳任何可拷贝的类型,主要通过类型擦除(Type Erasure)技术实现。当需要取出值时,必须进行严格的类型检查

例如:

#include <any> #include <iostream> #include <string> int main() { std::any a = 4; a = std::string("hello"); // 取值时必须显式指定期望的类型 std::cout << std::any_cast<std::string>(a) << "\n"; // 如果类型不匹配,会抛出 std::bad_any_cast 异常 // std::cout << std::any_cast<int>(a) << "\n"; // Error! }

2.4 std::string_view

它是一个轻量级的非拥有(non-owning)字符串引用。其内部仅包含一个字符指针和一个长度size

class string_view { const char* data_; // 指向外部内存 size_t size_; // 长度(不保证 \0 结尾) };

当需要传递字符串内容,而不需要修改和拥有它时,使用string_view可以避免std::string昂贵的内存分配和拷贝。

例如:

#include <iostream> #include <string_view> // 既能接收 std::string,也能接收 const char*,且没有任何拷贝开销 void print_first_3_chars(std::string_view str) { if (str.length() >= 3) { // substr 对于 string_view 也是 O(1) ,仅改变指针和长度 std::cout << str.substr(0, 3) << "\n"; } } int main() { std::string s = "abcdefg"; print_first_3_chars(s); // 不发生拷贝 print_first_3_chars("123456"); // 不发生分配和拷贝 }

三、编译期增强

编译期条件分支:constexpr if

传统的if是在运行时判断的,即使条件为假,分支内的代码也必须能够通过编译。if constexpr是在编译期求值。如果条件为假,编译器会直接丢弃该分支的代码(不参与编译)。这完美取代了以前复杂难懂的 SFINAE (如std::enable_if) 模板元编程技巧。

例如:

#include <iostream> #include <type_traits> template <typename T> void print_value(T value) { // 在编译期判断类型是否为指针 if constexpr (std::is_pointer_v<T>) { std::cout << "Pointer value: " << *value << "\n"; // 如果不是指针,这里解引用也不会导致编译错误 } else { std::cout << "Value: " << value << "\n"; } } int main() { int x = 11; print_value(x); // 编译时丢弃指针分支 print_value(&x); // 编译时丢弃非指针分支 }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 6:34:27

AI学习路径:从基础到实战的完整指南

1. 从零开始&#xff1a;AI学习者的认知重构作为转型AI的新手&#xff0c;首先要理解AI领域的全景图。AI并非单一技术&#xff0c;而是包含机器学习、深度学习、自然语言处理、计算机视觉等多个分支的庞大体系。2023年AGI&#xff08;通用人工智能&#xff09;的突破性进展&…

作者头像 李华
网站建设 2026/7/14 6:32:59

C++银行模拟系统实战:从面向对象设计到数据持久化

1. 项目概述&#xff1a;从零构建一个真实的银行模拟系统最近在整理硬盘&#xff0c;翻到了一个大学时期做的C银行模拟系统项目。当时为了完成课程设计&#xff0c;硬着头皮啃了好几本关于面向对象和数据结构的老书&#xff0c;熬了几个通宵才把核心功能跑通。现在回头看&#…

作者头像 李华
网站建设 2026/7/14 6:30:46

【jetson】串口通信测试和异常问题排查

本文完成 Jetson 串口收发功能测试&#xff0c;完整记录长报文接收异常故障的排查过程 一、环境说明 硬件&#xff1a;Jetson Orin Nano 40Pin排针系统&#xff1a;JetPack 6.2.2 / L4T R36.5串口&#xff1a;/dev/ttyTHS1&#xff08;硬件UARTA&#xff09;引脚对应 Pin8&…

作者头像 李华
网站建设 2026/7/14 6:30:13

Bently Nevada 133388-01(3500/53 超速保护监测模块)

一、基础信息完整型号&#xff1a;3500/53-02-00物料编号&#xff1a;133388-01定位&#xff1a;汽轮机、风机、压缩机专用电子超速保护模块&#xff0c;满足 API670、API612 机组安全规范&#xff0c;可构建 2 取 2 / 3 取 2 冗余表决 ETS 跳闸回路安装规格&#xff1a;3500 机…

作者头像 李华
网站建设 2026/7/14 6:29:10

【项目编号:project05966】SpringBoot/Vue 电影推荐系统:用户端推荐、电影资讯与后台数据管理全流程

摘要电影推荐系统是 JavaWeb 毕设和课程设计中搜索热度较高的项目类型。系统围绕用户端电影浏览、电影资讯、推荐展示、个人中心以及后台电影数据维护展开&#xff0c;既能体现 SpringBoot Vue 的完整开发流程&#xff0c;也能进一步扩展热门推荐、评分推荐和用户行为推荐等算…

作者头像 李华
网站建设 2026/7/14 6:26:26

深入解析栈缓冲区溢出:ASan原理、实战与防范指南

1. 项目概述&#xff1a;从一次崩溃说起如果你写过C&#xff0c;尤其是处理过数组、字符串或者复杂数据结构&#xff0c;大概率见过程序毫无征兆地崩溃&#xff0c;控制台只留下一句“Segmentation fault”或者直接闪退。更让人头疼的是&#xff0c;这种崩溃在开发环境里可能复…

作者头像 李华