← 返回博客列表

一、概述

C++17(ISO/IEC 14882:2017)是现代 C++ 三弹中的第二弹,整体定位是"把 C++11/14 画好的蓝图真正落地成能写业务的语言"。它没有发明太多底层概念,但一口气交付了结构化绑定、if/switch 初始化语句、折叠表达式、std::optional/variant/any、filesystem 文件系统、并行 STL 执行策略、string_view 等几十项"每天都能用到"的工程级特性,开发体验直接上了一个台阶。

二、核心语言大升级

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_ 的依赖。