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

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); // 编译时丢弃非指针分支 }
http://www.jsqmd.com/news/1185509/

相关文章:

  • Qt C++项目集成MeloTTS.cpp:纯C++离线TTS引擎的完整实践指南
  • AI项目部署实战:从环境配置到性能优化的完整指南
  • 机器学习实战入门:从西瓜书习题到核心概念通关
  • 从图像统计特征看图像质量评估-均值,方差,对比度,熵
  • AssetRipper:Unity资源逆向提取与恢复的实战指南
  • Python实现RC4流加密算法:从原理到可视化实战
  • 手把手教你用Python调优随机森林模型
  • 遗传算法工程化实战:从N皇后到生产级GA代码设计
  • Unity WebGL部署Tomcat报错:一个勾选解决Gzip压缩冲突
  • 分布级奖励优化:解决视觉生成模型奖励黑客与模式坍塌问题
  • 华为OD机试“猜密码”题解:回溯算法实现与多语言避坑指南
  • C++面向对象之多态的实现和应用详解
  • AI编程时代,PPT已成为过去,单个HTML文件创造无限可能
  • 怎么用 Claude 做 excel 表格 避坑指南,AI 导出鸭解决表格乱码、格式丢失难题
  • Pandas多维聚合实战:用MultiIndex构建可导航数据拓扑
  • LLM大模型中的概率分形理论与优化实践
  • STM32驱动压电蜂鸣器方案与优化实践
  • 扩散模型从入门到实战:2小时掌握DDPM核心原理与PyTorch实现
  • 嘎嘎降AI工具:学术论文降AI率实战指南
  • YOLOv8实战:构建Apex游戏人物识别检测系统完整教程
  • 十二、任务管理
  • 【STM32HAL开发】学习笔记(3)——串口
  • RUST编程学习-碎片2-Unsafe
  • 2026 年通辽优秀的叉车出租订做厂家哪家强,别再自己买!效率翻倍的秘密工具解析 - 企业官方推荐【认证】
  • 【Unity3D】TrailRenderer进阶:从导弹尾焰到武器挥砍的实战特效
  • 基于YOLOv8的道路坑洼检测系统:从模型训练到PyQT5界面开发全流程
  • C++ HTTP客户端库cpr:现代化网络请求的实践指南
  • Windows平台微信QQ防撤回工具RevokeMsgPatcher原理与实战指南
  • 【项目编号:project05966】SpringBoot/Vue 电影推荐系统:用户端推荐、电影资讯与后台数据管理全流程
  • Unity URP性能优化:SRP Batcher原理、配置与实战指南