本章把 C++17 的全部重要新特性集中讲一遍。 前面各章已经穿插了部分,这里系统汇总 + 补漏。
std::map<std::string, int> m{{"a", 1}, {"b", 2}};
for (auto [key, value] : m) { ... } // 遍历拆包
auto [x, y] = std::pair{3, 4}; // 拆 pair
auto [a, b, c] = std::tuple{1, 2.5, "s"}; // 拆 tuple
struct P { int x; int y; };
auto [px, py] = P{1, 2}; // 拆 struct 成员
也可以配合引用修改原对象:
for (auto& [k, v] : m) v *= 2;
它是语法糖,本质还是声明变量。注意:不能拆"类里有 私有成员且不是聚合"的对象,以及不能拆 string(没有绑定协议)。
if (auto it = m.find("x"); it != m.end()) {
// 使用 it
} // it 出了 if 就没了
switch (int v = get(); v) { ... }
好处:变量作用域收紧,不会"留着旧值"污染后面代码。
#include <optional>
std::optional<int> find_max(const std::vector<int>& v) {
if (v.empty()) return std::nullopt; // 没有最大值
return *std::max_element(v.begin(), v.end());
}
auto r = find_max(v);
if (r) { // 有值吗?
std::println("最大值 {}", *r);
std::println("最大值 {}", r.value_or(-1)); // 无值给默认
}
适用场景:函数"可能没有结果"(查找失败、空输入)。 比返回 -1 这种"魔法哨兵值"安全得多。
#include <variant>
std::variant<int, double, std::string> v;
v = 42; // 当前存 int
v = 3.14; // 现在存 double
v = "hi"; // 现在存 string
// 取出来:
if (auto p = std::get_if<int>(&v)) {
std::println("是整数 {}", *p);
}
// 或访问器(推荐):
std::visit([](const auto& x) { std::println("{}", x); }, v);
一句话:一个变量,多个类型轮流存,类型安全地访问。 比起 C 的 union(随意解释内存),variant 不会"看错类型"。
std::any a = 42;
a = std::string("hello");
int n = std::any_cast<int>(a); // 类型不对会抛异常
用途:极少数"类型完全未知"的场合。能用 variant 就别用 any。
零拷贝的字符串视图,参数传它,别传 const string&(拷贝)。 **别返回悬空的 string_view**。
#include <filesystem>
namespace fs = std::filesystem;
fs::path p = "data/in.txt";
p.extension(); // ".txt"
p.stem(); // "in"
p.parent_path(); // "data"
fs::exists(p); // 是否存在
fs::create_directories("a/b/c"); // 递归建目录
fs::copy(p, "backup.txt");
fs::remove("old.txt");
// 遍历目录(经典例子)
for (const auto& entry : fs::directory_iterator(".")) {
if (entry.is_regular_file())
std::println("{} ({} 字节)", entry.path().string(), entry.file_size());
}
可以递归遍历:fs::recursive_directory_iterator。 从此不再需要 system("ls") 之类的土办法。
编译期分支,模板编程必备。
if constexpr (std::is_pointer_v<T>) { ... }
变参模板求和/执行:
template <typename... Ts>
auto sum(Ts... args) { return (args + ...); } // 左折叠
inline constexpr int MAX_SIZE = 1024; // 头文件里也能放
inline int counter = 0;
没有 inline 的全局变量放在头文件里,多个 .cpp include 会 "重复定义"报错。inline 解决了(C++17)。
[[nodiscard]] int check() { return 42; }
check(); // 编译器警告:返回值被丢弃!
给"调用后必须处理结果"的函数(检查、错误码)加上, 防止忘处理返回值。也常用于容器的 empty() 等。
#include <print>
#include <filesystem>
#include <optional>
#include <vector>
std::optional<long long> file_size(const std::filesystem::path& p) {
std::error_code ec;
auto sz = std::filesystem::file_size(p, ec); // 不抛异常版
if (ec) return std::nullopt;
return sz;
}
int main() {
for (const auto& entry : std::filesystem::directory_iterator(".")) {
if (auto sz = file_size(entry.path()); sz) {
std::println("{} => {} 字节", entry.path().string(), *sz);
}
}
}
体会:optional 表达"可能失败",filesystem 表达"操作文件系统", if-with-initializer 收紧作用域——每个特性都让代码更短更安全。
练习题