C++17 不像 C++11 / C++20 那样「改天换地」,却塞进了大量日常极好用的工具:std::anystd::filesystem、结构化绑定、if constexpr、折叠表达式、并行算法……写业务代码时经常伸手就能摸到。

这一篇把 C++17 工具箱里最常用的一批串起来,对应三个 demo:any_demofilesystem_demoparallel_algo_demo

这是「现代 C++ 实战」系列的第 7 篇。建议先读 第 06 篇:Lambda 与类型推导

一、std::any:运行期存任意类型

C++17 的 std::any类型擦除容器——运行期可以换成 int、string、vector……类型信息保存在内部,取出时用 std::any_cast

1
2
3
4
5
6
7
8
9
#include <any>
#include <string>

std::any value = 42;
value = std::string("hello"); // 可以改存别的类型
value.emplace<std::vector<int>>({1,2,3});

int n = std::any_cast<int>(value); // 类型不对 → 抛 bad_any_cast
if (auto* p = std::any_cast<int>(&value)) { /* 安全,失败返回 nullptr */ }
成员 / 操作 作用
has_value() 是否为空
type() 返回 type_info
reset() 清空
any_cast<T>(a) 按类型取出(引用版抛异常,指针版返回 nullptr)

void*std::variant 怎么选?

void* std::variant<A,B,C> std::any
类型信息 编译期固定集合 运行期任意
内存 手动管理 通常栈上、无堆 大对象可能堆分配
典型场景 C 遗留接口 状态机、有限联合 配置表、插件返回值

选型:类型集合编译期已知 → variant(更快);运行期才知道类型 → any

demo 里用 map<string, any> 实现简易配置系统——set("port", 8080)get<int>("port"),见 ref/cpp_demo/basics/any_demo/

二、std::filesystem:跨平台文件系统

C++17 把文件系统操作收进标准库,告别「Windows 用一套 API、Linux 用另一套」:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <filesystem>
namespace fs = std::filesystem;

fs::path p = fs::current_path() / "data" / "config.json";
std::cout << p.filename() << p.extension() << '\n';

if (fs::exists(p)) {
std::cout << "size: " << fs::file_size(p) << '\n';
}

for (const auto& entry : fs::directory_iterator("logs")) {
if (entry.is_regular_file())
std::cout << entry.path() << '\n';
}

fs::create_directories("out/cache");
fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
常用 API 用途
fs::path 路径拼接 a / b、取 stem() / extension()
exists / file_size / last_write_time 属性查询
directory_iterator 单层目录遍历
recursive_directory_iterator 递归遍历
create_directories / remove_all 创建 / 删除
copy / copy_file / rename 复制与重命名

path 自动处理 /\,同一套代码在 macOS / Linux / Windows 上都能编译(链接时需 -lc++fs 的旧 GCC 除外)。

完整示例见 ref/cpp_demo/basics/filesystem_demo/

三、结构化绑定:一次拆多个返回值

C++17 允许把 pair、tuple、数组、结构体成员一次性绑定到多个变量:

1
2
3
4
5
6
7
8
9
10
std::map<std::string, int> m{{"a", 1}, {"b", 2}};
for (const auto& [key, value] : m) {
std::cout << key << " -> " << value << '\n';
}

std::pair<int, std::string> p{42, "ok"};
auto [code, msg] = p;

int arr[] = {1, 2, 3};
auto [x, y, z] = arr;

配合 range-for 遍历 map 特别顺手——不用再写 .first / .second

四、if constexpr:编译期分支

模板里按类型走不同逻辑,C++17 之前要靠 SFINAE;if constexpr不走的分支不参与实例化

1
2
3
4
5
6
7
8
9
10
template<typename T>
auto process(T value) {
if constexpr (std::is_integral_v<T>) {
return value * 2;
} else if constexpr (std::is_floating_point_v<T>) {
return value * 1.5;
} else {
return value;
}
}

if constexpr编译期决定走哪条分支;普通 if 两条分支都必须能编译通过。

五、折叠表达式:可变参数一行求和

C++17 折叠表达式简化了可变参数模板:

1
2
3
4
5
6
7
8
9
10
11
template<typename... Args>
auto sum(Args... args) {
return (args + ...); // 一元右折叠
}

sum(1, 2, 3, 4); // 10

template<typename... Args>
bool all_true(Args... args) {
return (args && ...);
}
形式 示例 含义
一元右折叠 (args + ...) arg1 + (arg2 + (...))
一元左折叠 (... + args) ((...) + arg2) + arg1
二元折叠 (args + ... + 0) 带初始值

六、并行算法:std::execution::par

C++17 为部分 STL 算法增加了执行策略参数:

1
2
3
4
5
6
7
8
9
10
#include <algorithm>
#include <execution>
#include <vector>

std::vector<int> v(1'000'000);
std::sort(std::execution::par, v.begin(), v.end());

std::transform(std::execution::par_unseq,
a.begin(), a.end(), b.begin(),
[](int x) { return x * x; });
策略 含义
std::execution::seq 顺序(默认行为)
std::execution::par 允许多线程并行
std::execution::par_unseq 并行 + 允许 SIMD 向量化

注意

  • 数据量小时并行反而更慢(线程开销)
  • par_unseq 要求操作无锁、无分配——纯计算最合适
  • Apple Clang / libc++ 可能头文件存在但未真正实现并行策略;demo 用 __cpp_lib_parallel_algorithm 宏检测,不支持时回退顺序执行

并行算法底层通常用标准库自带的线程池,不是你自己写的线程池(系列第 14 篇会讲自建线程池)。

性能对比见 ref/cpp_demo/basics/parallel_algo_demo/——对百万级 sort / reducechrono 计时。

七、三个 demo 怎么跑

1
2
3
cd ref/cpp_demo/basics/any_demo && ./build.sh --run
cd ref/cpp_demo/basics/filesystem_demo && ./build.sh --run
cd ref/cpp_demo/basics/parallel_algo_demo && ./build.sh --run

CMake 里记得 set(CMAKE_CXX_STANDARD 17);filesystem 在 GCC 8 以前可能需要链接 stdc++fs

八、小结

特性 一句话
std::any 运行期任意类型,配置 / 插件场景
std::filesystem 跨平台路径与文件操作
结构化绑定 auto [k,v] 解构 map / pair
if constexpr 模板内编译期分支
折叠表达式 (args + ...) 简化可变参数
并行算法 std::execution::par 加速大数组算法

现代 C++ 实战系列第 7 篇完。下一篇 错误处理策略——错误码、异常、optional、expected 各走哪条路。

系列导航

篇号 标题 状态
06 Lambda 与类型推导
07 C++17 工具箱(本篇)
08 错误处理策略 下一篇

完整大纲见工作区 docs/CPP_SERIES_OUTLINE.md