一、概述
C++17(ISO/IEC 14882:2017)是现代 C++ 三弹中的第二弹,整体定位是"把 C++11/14 画好的蓝图真正落地成能写业务的语言"。它没有发明太多底层概念,但一口气交付了结构化绑定、if/switch 初始化语句、折叠表达式、std::optional/variant/any、filesystem 文件系统、并行 STL 执行策略、string_view 等几十项"每天都能用到"的工程级特性,开发体验直接上了一个台阶。
目录
- 结构化绑定 Structured Bindings
- if / switch 初始化语句
- constexpr Lambda
- inline 变量(解决头文件全局静态/const 链接错误)
- 折叠表达式 Fold Expressions
- 类模板参数推导 CTAD
- [[nodiscard]] / [[maybe_unused]] / [[fallthrough]]
- auto 非类型模板参数 + constexpr if
- 三剑客 I:std::optional
- 三剑客 II:std::variant(类型安全联合)
- 三剑客 III:std::any
- std::string_view 零拷贝字符串视图
- std::filesystem 文件系统
- shared_mutex 无超时读写锁
- 并行 STL 算法 ExecutionPolicy
- 实用小工具(apply / clamp / not_fn / size/data/empty 全局)
二、核心语言大升级
2.1 结构化绑定 Structured Bindings
语法革命把 tuple / pair / struct / 数组 / map 的元素"一次性拆包"到多个具名变量。写 C++ 再也不用痛苦地 tie(a, b, ignore, c) = xxx,也不用每次 auto& kv = *it; kv.first / kv.second。
#include <tuple>
#include <map>
#include <string>
struct Point3D { double x, y, z; };
Point3D getCenter() { return {1.0, 2.0, 3.0}; }
auto getUser() {
return std::tuple<std::string, int, double>("Yang", 30, 178.5);
}
int main() {
// (1) 绑定到 POD / struct 成员变量
auto [cx, cy, cz] = getCenter(); // cx=1.0, cy=2.0, cz=3.0 直接拿到具名变量
// (2) 绑定到 tuple / pair(替代 std::tie)
auto [name, age, height] = getUser(); // name="Yang" age=30 height=178.5
// (3) 绑定到原生数组
int arr[] = {10,20,30};
auto [a, b, c] = arr; // a=10, b=20, c=30(按值拷贝)
auto& [ra, rb, rc] = arr; // 引用绑定,修改 ra=999 会直接改 arr[0]
// (4) 最常用:遍历 map(再也不用 it->first / it->second 了!)
std::map<int, std::string> mp = {{1,"one"}, {2,"two"}};
for (const auto& [key, val] : mp) {
std::cout << key << " → " << val << "\n";
}
// (5) 插入 map 的返回值(pair<iterator, bool>)直接解构
auto [it, inserted] = mp.insert({3, "three"});
if (inserted) { /* 插入成功 */ }
}
2.2 if / switch 初始化语句
代码紧凑在 if / switch 的括号里先写一条初始化语句(和 for 循环结构完全一致),把临时变量的作用域牢牢锁在 if 块里,避免外层命名空间被污染。最经典是 if (auto it = m.find(k); it != m.end())。
std::map<std::string, int> scores = {{"Alice",95}, {"Bob",78}};
// C++11/14:it 作用域泄露到外层函数
{
auto it = scores.find("Alice");
if (it != scores.end()) { /* use it */ }
// it 依然可见,继续存在直到外层结束
}
// C++17 if 初始化:it 的作用域只在 if / else 内
if (auto it = scores.find("Bob"); it != scores.end()) {
it->second += 5; // 加分
} else {
// it 仍然可见(与初始化变量同作用域)
}
// ✓ 离开 if,it 被销毁,不会污染外层命名
// 另一个常见场景:锁
if (std::lock_guard<std::mutex> lk(mtx); condition) {
// 持锁下执行一段临界区,if 退出自动释放锁
}
// switch 初始化:常见于状态机 enum + 状态码
switch (int code = readErrorCode(); code) {
case 0: /* OK */ break;
case -1: /* retry */ break;
default: /* log */ break;
}
2.3 constexpr Lambda
编译期能力lambda 终于可以在编译期(constexpr 语境)使用了,只要 lambda 体不做任何"非 constexpr"动作(new、动态多态、虚函数调用…)。从此 STL 的 constexpr 算法终于能配 lambda 写大段编译期逻辑。
// C++14 lambda 不能在编译期用,C++17 可以
constexpr int sumOfSquares(int n) {
auto sq = [](int x) constexpr { return x * x; };
int total = 0;
for (int i = 1; i <= n; ++i) total += sq(i);
return total;
}
static_assert(sumOfSquares(3) == 14); // 1 + 4 + 9 = 14 ✅ 编译期验证通过
// C++17 顺便给 lambda 加上了 "=default" 赋值运算符(当捕获为空时)
int main() {
auto cmp = [](int a, int b) { return a < b; };
auto cmp2 = cmp; // OK:默认拷贝
cmp2 = cmp; // C++17:无捕获 lambda 可赋值(= default)
}
2.4 inline 变量
头文件救星解决 C++ 一个被骂了几十年的老问题:想在头文件里放一个全局 constexpr 复杂对象 / ODR 全局单例,但在多个 .cpp 里 include 会重定义链接错误。C++17 inline 变量直接让它和 inline 函数一样,"多处定义由链接器挑一份就行"。类内静态成员变量也不再需要单独写一个 .cpp 中的 Type Class::member = xxx 定义。
/* ===== my_constants.h (头文件)===== */
#pragma once
#include <string>
// C++14:constexpr int MAX_RETRY = 3; OK(字面量,constexpr 整数 OK)
// constexpr std::string NAME = "global"; ❌ 链接错误!每个 TU 一份
// C++17 用 inline 变量:完全可直接写在头里,任意多 .cpp include 都 OK
inline constexpr int MAX_RETRY = 3;
inline const std::string APP_NAME = "RoboServer v2"; // ✅ 字符串放头文件也没问题
// 另一经典:类静态 constexpr 成员变量定义终于不用放到 .cpp 了
struct Settings {
static inline constexpr double PI = 3.1415926535; // C++17 内联静态成员,头内直接写死
};
/* ===== 以上写在头里 OK!之前必须在 settings.cpp 写一行:constexpr double Settings::PI;*/
2.5 折叠表达式 Fold Expressions
模板元编程操作模板参数包 Ts... args 再也不用写递归来一个个展开了,折叠表达式用 (args op ...) / (... op args) / (init op ... op args) 四种形式,一行搞定可变参数的"连加 / 连乘 / 逗号依次执行 / 连续 cout 输出"。
// 1) 一元右折叠(对参数包递归运算):(args + ...) = a0 + (a1 + (a2 + ... + aN))
template<typename... Args>
auto sum(Args... args) {
return (args + ...); // ✅ 一行搞定 sum(1,2,3,4) = 10
}
// 2) 一元左折叠:(... + args) = (((a0 + a1) + a2) + a3) + ... + aN
template<typename... Args>
void printAll(std::ostream& os, Args... args) {
(os << ... << args) << '\n'; // 连续把所有 args 喂给 ostream
}
// 3) 二进制折叠(带初始值):避免空包时的未定义,并统一初始哨兵
template<typename... Args>
auto product(Args... args) {
return (1 * ... * args); // 空包 → 1,不会错
}
// 4) 逗号折叠:对每一个参数分别执行同一操作(最常用:把参数包都 push_back 进 vector)
template<typename T, typename... Args>
void pushAll(std::vector<T>& vec, Args... args) {
(vec.push_back(args), ...); // 逗号表达式折叠:依次执行每个 push_back
}
int main() {
sum(1,2,3,4); // → 10
printAll(std::cout, "a=", 10, " b=", 20); // 输出 a=10 b=20
product(2,3,4,5); // → 120
std::vector<int> v;
pushAll(v, 1,2,3,4,5); // v → [1,2,3,4,5]
}
2.6 类模板参数推导 CTAD(Class Template Argument Deduction)
简洁函数模板能自动推导参数类型已经几十年了,类模板直到 C++17 才支持"从构造函数参数推导模板参数"。从此写 std::pair p(1, 2.0); 不用再写 std::make_pair,std::lock_guard lk(mtx); 也不再要 std::lock_guard<std::mutex>。
/* C++11/14:必须显式写类模板参数(啰嗦)*/
std::pair<int, double> p1(1, 3.14);
std::vector<int> v1{1,2,3,4,5};
std::lock_guard<std::mutex> lck1(mtx);
/* C++17 CTAD:直接让编译器推导模板参数(和函数模板一样)*/
std::pair p2(1, 3.14); // 推导出 pair<int, double>
std::vector v2{1,2,3,4,5}; // 推导出 vector<int>
std::lock_guard lck2(mtx); // 推导出 lock_guard<std::mutex>
std::tuple tup(1, "abc", 9.9); // 推导出 tuple<int, const char*, double>
// 自定义类也能自动推导,还能写用户自定义推导指引 Deduction Guide
template<typename T>
struct MyVec {
MyVec(T first, T second) { data_[0] = first; data_[1] = second; }
T data_[2];
};
MyVec mv(10, 20); // ✅ 自动推导 MyVec<int>
2.7 三个标准化属性:[[nodiscard]] / [[maybe_unused]] / [[fallthrough]]
代码质量把常见编译器扩展统一标准化:nodiscard 检查返回值是否被忽略(最适合错误码、owner_ptr、optional 这种"不处理就 bug"的类型);maybe_unused 告诉编译器"我故意没用到,别警告";fallthrough 告诉编译器 switch 的 case 穿透是故意的。
/* 1) [[nodiscard]]:典型用于错误码、unique_lock 返回值、allocate 等 */
[[nodiscard]] int sendPacket(const char* data, size_t len) {
// 返回 0=成功, 负数=错误码(必须让调用者判断)
return -1;
}
[[nodiscard("必须检查连接是否建立成功")]] // C++20 起支持字符串原因,部分编译器 C++17 扩展支持
bool connectTo(const std::string& host);
int main() {
// sendPacket("hi", 2); // ❌ 编译器警告:nodiscard 返回值被丢弃
if (sendPacket("hi", 2) < 0) { /* 重试 */ } // ✅ 使用了返回值,无警告
}
/* 2) [[maybe_unused]]:故意不用,别警告(函数参数、变量、枚举器)*/
void callback(int a, [[maybe_unused]] int b) { // b 因为兼容性保留,但当前版本不用
return a * 2;
}
[[maybe_unused]] constexpr int DEBUG = 0; // 宏未使用时也不会报 warning
/* 3) [[fallthrough]]:switch 两个 case 穿透是故意的(消除 -Wimplicit-fallthrough)*/
switch (level) {
case WARN:
logWarn();
[[fallthrough]]; // ✅ 故意穿透到 ERROR,告诉编译器不要警告
case ERROR:
notifyOps();
break;
default: break;
}
2.8 auto 非类型模板参数 & constexpr if
模板元编程auto NTTP:模板非类型参数(如 template<int N>)可以直接写 template<auto N>,让 N 的类型由实参自动推导。
constexpr if:if constexpr (cond) { A } else { B },编译期分支裁剪——被舍弃的分支完全不实例化。C++11/14 要用 SFINAE 写 30 行的类型分派,现在 3 行搞定。
/* (1) template <auto N> 自动推导非类型参数 */
template<auto MagicValue> // MagicValue 的类型由调用时传进来的值决定
void magicFn() {
std::cout << "Magic = " << MagicValue
<< " (type size=" << sizeof(MagicValue) << "B)\n";
}
// magicFn<1024>(); // N = int
// magicFn<42ULL>(); // N = unsigned long long
/* (2) constexpr if:编译期分派 —— SFINAE 的大救星!*/
template<typename T>
auto lenOrSize(const T& x) {
// 编译期检查 T 有没有 size() 成员函数(constexpr 条件为 bool)
if constexpr (requires { x.size(); }) { // C++20 requires,C++17 等价写法是 is_detected 或 SFINAE
return x.size(); // ✅ 有 size() 就调用
} else {
return sizeof(x); // ✅ 没 size() 的就退化成 sizeof,不会编译错
}
}
// C++17 原生写法(不使用 requires 语法):结合 type_traits + constexpr if
#include <type_traits>
template<typename T>
auto asIntegral(T x) {
static_assert(std::is_arithmetic_v<T>, "T 必须是算术类型");
if constexpr (std::is_integral_v<T>) {
return x; // T 是整数 → 原样返回
} else {
return static_cast<long long>(x); // 浮点 → 提升到 long long
}
}
三、标准库三剑客:optional / variant / any
3.1 std::optional<T>(可空值)
工程安全替代"用 -1 / nullptr / false 表示没找到"的编码约定。语义明确表示"值可能存在,也可能不存在"——函数失败时返回空 optional,而不是抛异常/返回魔法值;调用方用 has_value() 或 operator bool 检查,再 value() / value_or(default) 获取。
#include <optional>
#include <string>
#include <unordered_map>
#include <iostream>
// 以前:用 pair<bool, T> / 返回 -1 / 传指针输出参数
// 以后:返回 optional(明确表达"可能有、也可能没有")
std::optional<int> safe_divide(int a, int b) {
if (b == 0) return std::nullopt; // 没结果:返回"空 optional"
return a / b; // 有结果:自动包装成 optional<int>
}
template<typename K, typename V>
std::optional<V> lookup(const std::unordered_map<K, V>& m, const K& k) {
auto it = m.find(k);
return it == m.end() ? std::nullopt : std::optional<V>(it->second);
}
int main() {
auto ans = safe_divide(10, 0);
if (!ans) {
std::cout << "除零,无结果\n";
} else {
std::cout << "结果 = " << *ans << '\n'; // 或者 ans.value(),空的会抛 bad_optional_access
}
// 给默认值兜底,调用链非常清爽
int score = safe_divide(100, 3).value_or(0); // 有值取 33,空则取默认 0
}
3.2 std::variant<Ts...>(类型安全联合,tagged union)
类型安全彻底替代 C 时代 union + tag。variant 同时只能持有 Ts 其中一种类型,标准库提供 holds_alternative / get / get_if / visit 四大 API,访问未激活成员会直接抛异常(UB 杜绝)。是实现代数数据类型 ADT、状态机状态、AST 节点、消息变体的第一选择。
#include <variant>
#include <string>
#include <iostream>
struct Circle { double r; };
struct Rect { double w, h; };
struct Triangle { double a, b, c; };
// 一个 Shape 变量可以"在运行时"是 Circle / Rect / Triangle 之一,但永远不是两种同时存在
using Shape = std::variant<Circle, Rect, Triangle>;
/* 用 std::visit + 重载 Lambda 实现多态分发(推荐做法)*/
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>; // CTAD 推导指引
double area(const Shape& s) {
return std::visit(overloaded{
[](const Circle& c) { return 3.14159 * c.r * c.r; },
[](const Rect& r) { return r.w * r.h; },
[](const Triangle& t) { // 海伦公式
double p = (t.a + t.b + t.c) / 2.0;
return std::sqrt(p*(p-t.a)*(p-t.b)*(p-t.c));
},
}, s);
}
int main() {
Shape s1 = Circle{2.0};
Shape s2 = Rect{3.0, 4.0};
// Shape s3 = 123; // ❌ 编译错误:int 不是 variant 的备选项,不再像 union 一样不安全
// 直接类型判断 + 取值
if (auto* pCircle = std::get_if<Circle>(&s1)) {
std::cout << "圆半径 = " << pCircle->r << "\n";
}
std::cout << area(s1) << "\n"; // 12.566
std::cout << area(s2) << "\n"; // 12
}
3.3 std::any(任意类型容器,单继承类型擦除)
动态类型能装下任意可拷贝类型的值的"通用盒子"。和 void* 的区别:any 内部持有 type_info,any_cast 取错类型会直接抛 bad_any_cast 异常而不是崩溃。一般用于插件框架参数、配置表异值列、属性树叶子节点等"真正无法用模板 + variant 约束类型集合"的场景。
#include <any>
#include <vector>
#include <string>
#include <iostream>
class PropertyBag {
std::unordered_map<std::string, std::any> props_;
public:
template<typename T>
void set(const std::string& key, T value) { props_[key] = std::move(value); }
template<typename T>
std::optional<T> get(const std::string& key) const {
auto it = props_.find(key);
if (it == props_.end()) return std::nullopt;
try { return std::any_cast<T>(it->second); }
catch (const std::bad_any_cast&) { return std::nullopt; }
}
};
int main() {
PropertyBag bag;
bag.set("name", std::string("Yang"));
bag.set("age", 30);
bag.set("scores", std::vector<int>{90,85,92});
auto maybeAge = bag.get<int>("age"); // → optional(30)
auto wrongType = bag.get<double>("age"); // → nullopt (不抛异常,封装良好)
}
四、实用库:string_view / filesystem / shared_mutex / 并行 STL
4.1 std::string_view 零拷贝字符串视图
性能只是"指向外部字符缓冲区 + 长度"的胖指针(两个机器字),不持有内存所有权。把它当函数参数,可以统一接受 const char* / std::string / 子串 而不必产生临时 std::string 分配(这是 C++17 性能提升最大的单一库特性)。
#include <string_view>
#include <string>
#include <iostream>
// 之前:写 const std::string& 但传 const char* 时会分配临时 string(性能坑)
// bool startsWith(const std::string& s, const std::string& prefix); */
// C++17 之后:统一使用 std::string_view,零拷贝 / 零分配 / 三种输入皆可吃
bool startsWith(std::string_view s, std::string_view prefix) {
return s.size() >= prefix.size() && s.substr(0, prefix.size()) == prefix;
}
int main() {
const char* c_str = "https://example.com/path";
std::string str = "https://acme.cn/search";
startsWith(c_str, "https://"); // ✅ const char* 直接吃,无临时 string 分配
startsWith(str, "https://"); // ✅ std::string 直接吃
// substr() 也零拷贝:返回新 string_view(只改指针/长度)
std::string_view url(c_str);
auto schema = url.substr(0, 5); // 指向原内存,不分配新串
// ⚠️ 注意:string_view 不拥有内存,严禁:
// 1) 把函数内局部字符串当 string_view 返回;
// 2) 把原字符串销毁后再访问 view。
}
4.2 std::filesystem 文件系统(终于标准化!)
跨平台彻底替代 boost.filesystem 和平台相关的 _findfirst / opendir / CreateFile 遍历。path 类自动处理正斜杠反斜杠、拼接 / 分割、扩展名操作;directory_iterator / recursive_directory_iterator 递归遍历;copy / create_directories / remove_all / file_size / last_write_time / exists / is_directory 等几十个 API 一次性打包。
#include <filesystem>
#include <iostream>
#include <chrono>
namespace fs = std::filesystem; // 大家习惯起别名
uintmax_t dirTotalSize(const fs::path& dir) {
uintmax_t total = 0;
for (auto& entry : fs::recursive_directory_iterator(dir)) { // 递归遍历所有子目录
if (entry.is_regular_file()) total += entry.file_size();
}
return total;
}
int main() {
// (1) path 拼接:跨平台自动处理分隔符(Windows \, Linux /)
fs::path base = "C:/Users/yang";
fs::path log = base / "logs" / "app.log"; // operator/ 惯用:自动加分隔符
// Windows 得到 "C:\Users\yang\logs\app.log"
fs::create_directories(log.parent_path()); // 创建缺失的中间目录
// (2) 判断文件状态、时间、大小
if (exists(log)) {
std::cout << "大小: " << fs::file_size(log) << " B\n";
std::cout << "扩展名: " << log.extension().string() << "\n"; // ".log"
}
// (3) 递归计算当前目录总大小(工程常用)
auto size = dirTotalSize("./");
std::cout << "当前目录总大小 ≈ " << (size >> 20) << " MB\n";
}
4.3 std::shared_mutex(无超时读写锁)
并发库C++14 提供的是 timed 版本(每个 lock/unlock 都带重载等待时间),C++17 去掉超时 API,换用更轻量的纯读写锁实现,GCC/Linux 下基于 pthread_rwlock,在多读少写的场景(配置表、缓存、路由表)比 mutex 吞吐高一个数量级。
#include <shared_mutex> // C++17 的 shared_mutex(对比 C++14 shared_timed_mutex)
#include <vector>
#include <string>
class RoutingTable {
mutable std::shared_mutex rw_;
std::vector<std::string> table_;
public:
// 读:100 线程并行查也不会互相阻塞
bool hasRoute(const std::string& dst) const {
std::shared_lock<std::shared_mutex> read(rw_);
return std::find(table_.begin(), table_.end(), dst) != table_.end();
}
// 写:独占;全局只能一个线程更新,任何 reader 都得等它结束
void addRoute(const std::string& dst) {
std::unique_lock<std::shared_mutex> write(rw_);
table_.push_back(dst);
}
};
4.4 并行 STL 算法 ExecutionPolicy
多核性能在所有经典算法(sort / for_each / transform / reduce / copy_if 等)前面加一个"执行策略"参数,就能切换成单线程 / 多线程并行 / 向量化并行三种模式,彻底告别手写 OpenMP / ThreadPool。头 <execution>,三种策略:seq(单线程)/ par(并行)/ par_unseq(并行+向量化)。
#include <execution>
#include <algorithm>
#include <vector>
#include <numeric>
int main() {
const int N = 10'000'000;
std::vector<double> data(N);
std::iota(data.begin(), data.end(), 0.0); // 0,1,2,3,...,N-1
// 1) 经典单线程串行(原来的写法)
std::sort(data.begin(), data.end());
// 2) 多线程并行:内部开线程池切分 + 合并 sort(8-16 核机器 5-10x 加速)
std::sort(std::execution::par, data.begin(), data.end());
// 3) 并行 + 向量化(允许编译器用 SIMD,对连续数组类操作效果最大)
std::transform(std::execution::par_unseq,
data.begin(), data.end(), data.begin(),
[](double x) { return x * x + 3.0 * x + 1.0; });
// 4) 并行 reduce:多线程归约求和
double sum = std::reduce(std::execution::par, data.begin(), data.end(), 0.0);
}
4.5 实用小工具合集:std::apply / clamp / not_fn / 全局 std::size/data/empty
小而美把常见小工具统一进标准库:apply 用 tuple 做函数实参包调用;clamp 把值钳制在 [lo,hi];not_fn 翻转谓词返回 bool;std::size/data/empty 全局函数,对 C 数组/STL容器/initializer_list 统一可用。
#include <tuple>
#include <algorithm>
#include <functional>
#include <iterator>
#include <vector>
#include <string>
void logRecord(int id, const std::string& name, double score) {
/* 写入数据库 */
}
int main() {
// (1) std::apply:把 tuple 按元素依次填入函数参数(内部用 C++14 index_sequence)
auto row = std::make_tuple(1001, "Alice", 98.5);
std::apply(logRecord, row); // 等价于 logRecord(1001, "Alice", 98.5)
// (2) clamp(value, lo, hi):超过边界就截断到边界,中间保持原值
double pixelR = 280.0;
pixelR = std::clamp(pixelR, 0.0, 255.0); // 钳制到 255.0(越界修正)
// (3) not_fn:翻转任何谓词的布尔结果
auto isOdd = [](int x) { return x % 2 == 1; };
auto isEven = std::not_fn(isOdd); // 直接生成一个"偶数"谓词,不用再写一遍取反 lambda
// (4) 全局 std::size / data / empty:统一吃 STL 容器 + C 原生数组
int arr[] = {1,2,3,4};
std::vector v = {1,2,3};
auto n1 = std::size(arr); // = 4(以前只能写 sizeof(arr)/sizeof(arr[0]))
auto n2 = std::size(v); // = 3
auto p = std::data(arr); // → int* 指向首元素(对 string/vector 返回 .data())
bool e = std::empty(v); // = false
}
五、C++17 特性总览对照表
| 分类 | 特性 | 一句话价值 |
|---|---|---|
| 核心语言 | 结构化绑定 | tuple/struct/map 一键解构 |
| if / switch 初始化 | 作用域收缩,if 块用完变量自动销毁 | |
| constexpr Lambda | 编译期可用 lambda + STL constexpr 算法 | |
| inline 变量 | 头文件全局变量告别链接错误 | |
| 折叠表达式 | 可变参数包一行展开,模板递归可以退休 | |
| CTAD 类模板参数推导 | make_xxx 辅助函数可以省掉了 | |
| [[nodiscard / maybe_unused / fallthrough]] | 三大代码质量属性标准化 | |
| auto NTTP + constexpr if | 非类型模板参数自动推导;编译期类型分派 | |
| 三剑客 | std::optional | 语义化"可能没有值",替代魔法值 / 指针输出 |
| std::variant | 类型安全 union,visitor 多态分发 | |
| std::any | 运行时类型安全的"装任何东西"容器 | |
| 实用库 | std::string_view | 字符串子串零拷贝,消除大量 malloc 热点 |
| std::filesystem | 跨平台文件遍历/操作/递归目录/路径拼接 | |
| std::shared_mutex | 无超时读写锁,多读少写吞吐高 | |
| 并行算法 ExecutionPolicy | 一行 seq/par/par_unseq,算法秒变并行 SIMD | |
| 小工具 | apply / clamp / not_fn / size(data(empty)) | 工程常用工具统一进标准 |
总结 & 升级建议
- 收益最大、最该先推广:string_view(函数签名全面替换)、结构化绑定、if/switch 初始化。这三项几乎不会带来学习成本,却能让代码立刻清爽很多。
- 代码质量 + 类型安全:[[nodiscard]] 给所有错误码返回值;variant 全面替换 C union 和 void*;optional 替换一切"bool 输出 + 引用参数/指针返回"的老 C 风格接口。
- 性能突破:对热点 sort/transform/reduce 直接加 par / par_unseq,在多核服务器上可能获得 5~15 倍性能收益;shared_mutex 在配置/缓存场景替代 mutex。
- 跨平台工程:filesystem 替换掉历史遗留的 Boost.Filesystem / FindFirstFile 等平台特定实现,编译脚本也可以简化出 BOOST_ 的依赖。