一、引言
C++17 是 C++ 发展史上的一个重要里程碑,它引入了大量旨在简化日常编程、提升代码可读性以及增强性能的新特性。
以下是 C++17 的全面分点总结,主要分为核心语言特性、标准库增强以及属性与宏三个方面:
二、核心语言特性 (Core Language Features)
-
结构化绑定 (Structured Bindings)
允许将元组(Tuple)、对(Pair)、数组或结构体的成员直接绑定到多个变量上,极大地简化了多返回值的提取。
示例:
auto [x, y, z] = get_tuple();或for (auto& [key, value] : my_map) { ... } -
带初始化的
if和switch语句允许在
if和switch的条件判断前声明并初始化变量,从而将变量的作用域严格限制在代码块内,保持外部命名空间干净。示例:
if (auto it = m.find(key); it != m.end()) { ... } -
内联变量 (Inline Variables)
引入了
inline变量修饰符。过去在头文件中定义类的静态成员变量时,必须在对应的.cpp文件中进行初始化(否则会导致多重定义错误)。现在可以直接在头文件中使用inline初始化静态成员。示例:
class MyClass { static inline int count = 0; }; -
折叠表达式 (Fold Expressions)
极大地简化了可变参数模板(Variadic Templates)的编写,允许对参数包中的所有参数直接应用二元操作符,而不再需要写繁琐的递归模板。
示例:
template<typename... Args> auto sum(Args... args) { return (args + ...); } -
编译期
if语句 (if constexpr)允许在编译期进行条件分支判断。如果不满足条件,该分支的代码将不会被实例化。这让模板元编程变得像普通代码一样简单,取代了许多复杂的 SFINAE(替换失败并非错误)技巧。
-
类模板参数推导 (CTAD - Class Template Argument Deduction)
实例化类模板时,编译器可以根据构造函数的参数自动推导模板类型,不再需要强制写出类型参数或使用
std::make_xxx辅助函数。示例:以前写
std::pair<int, double> p(1, 2.0);,现在只需写std::pair p(1, 2.0); -
Lambda 表达式增强
-
捕获
*this的值: 允许通过[*this]语法按值捕获当前对象。这在多线程或异步编程中非常重要,可以避免由于对象生命周期结束导致的悬空指针问题。 -
constexprLambda: 只要 Lambda 函数体满足条件,它就可以被声明为constexpr并在编译期执行。
-
-
嵌套命名空间简化
提供了一种更简洁的方式来定义多层嵌套的命名空间。
示例:以前写
namespace A { namespace B { namespace C { ... } } },现在可以直接写namespace A::B::C { ... } -
保证的拷贝消除 (Guaranteed Copy Elision / RVO)
C++17 标准在语法层面上强制保证:在使用临时对象(纯右值 prvalue)初始化同类型对象时,绝不发生拷贝或移动操作。这意味着你甚至可以返回不可拷贝和不可移动的对象(如
std::mutex)。 -
更严格的表达式求值顺序
规范了部分表达式(如赋值表达式、函数参数求值等)的执行顺序,消除了许多未定义行为(Undefined Behavior),让代码更安全。
三、标准库新特性 (Standard Library Features)
-
std::optional提供了一种类型安全的方式来表示"可能不存在的值"。非常适合用于替代返回指针(可能为空)或使用特殊值(如
-1)来表示失败的函数设计。 -
std::variant类型安全的、更现代的
union替代品。它可以存储指定类型列表中的任意一种类型,并且知道当前存储的是哪种类型,不会发生传统联合体的类型越界问题。 -
std::any一个可以存储任意 类型的容器(类型安全的
void*)。与variant不同,any不需要提前声明可能包含的类型列表。 -
std::string_view提供了一个轻量级的、非拥有的(Non-owning)字符串视图。它可以绑定到
std::string或 C 风格字符串上,由于只包含指针和长度信息,在传递和截取字符串时可以避免昂贵的内存拷贝,大幅提升性能。 -
std::filesystem(文件系统库)终于将跨平台的文件系统操作纳入标准库。提供了路径解析(
path)、文件读写状态检查、目录遍历(directory_iterator)、文件复制/删除等强大的文件操作能力。 -
并行算法 (Parallel Algorithms)
为
<algorithm>库中的大部分算法(如std::sort,std::for_each,std::transform等)添加了执行策略参数。通过传入std::execution::par等策略,可以极其方便地将单线程算法转化为多线程并行执行。 -
std::byte提供了一种区分于字符(
char)和整数(int)的类型,专门用于表示原始内存数据(Raw Memory)。它只能进行按位运算,没有算术运算,提升了类型安全性。 -
std::clamp一个简单的辅助函数,用于将一个值限制在给定的最小值和最大值之间(即钳制操作)。
-
Map/Set 节点的提取与合并 (Node Splicing)
引入了
.extract()和.merge()方法,允许直接将节点从一个std::map或std::set移动到另一个同类型容器中,而不发生任何内存分配或拷贝。
四、属性与宏 (Attributes & Macros)
-
三大新属性 (Attributes)
-
[[nodiscard]]: 用于修饰函数或类型。如果调用者忽略了该函数的返回值,编译器会发出警告。常用于错误码返回或资源分配函数。 -
[[fallthrough]]: 明确告诉编译器在switch语句中,此处的case故意不写break并向下贯穿,从而消除编译器的警告。 -
[[maybe_unused]]: 用于声明可能不会被使用的变量、函数或参数,抑制编译器发出"变量未使用"的警告。
-
-
__has_include宏允许在预处理阶段检查某个特定的头文件是否存在。这使得编写跨平台或跨版本兼容的代码变得更加容易。
示例:
#if __has_include(<filesystem>) -
十六进制浮点数字面量
允许以十六进制形式精确表示浮点数(例如
0x1.P-1022),避免了十进制和二进制浮点数转换时的精度损失问题。