C++23 新特性全面总结
核心思想:C++23 延续了 C++20 的现代化进程,被定位为"C++20 的完善版"。它没有引入全新的四大旗舰,而是围绕 C++20 生态进行深度打磨与补齐:语言层面引入了革命性的 Deducing this(显式对象参数) 、if consteval、多维下标运算符等;标准库层面补齐了 std::expected、std::generator、std::print、std::flat_map、std::mdspan、大量 Ranges 视图以及 std::optional 的 Monadic 操作等长期呼声极高的组件。C++23 让现代 C++ 的开发体验更加流畅、安全和富于表达力。
🚀 1. Deducing this ------ 显式对象参数(最重要的语言特性)
1.1 问题与动机:
C++
/*
* C++20 及之前的痛点:
* ┌─────────────────────────────────────────────────────────────────┐
* │ 1. const / 非const 重载几乎完全重复代码 │
* │ 2. CRTP(奇异递归模板模式)写法笨重 │
* │ 3. Lambda 无法递归调用自身 │
* │ 4. 按值类别完美转发 *this 需要大量样板代码 │
* │ 5. 同一逻辑的 &、const&、&&、const&& 四个重载令人崩溃 │
* └─────────────────────────────────────────────────────────────────┘
*/
// ❌ C++20:const / 非const 的代码重复
class TextBuffer_old {
std::string data_;
public:
// 几乎完全相同的两个重载
const char& at(size_t i) const {
// 边界检查...
return data_[i];
}
char& at(size_t i) {
// 相同的边界检查...(重复!)
return data_[i];
}
// 更极端:按值类别返回不同的东西
const std::string& get() const& { return data_; }
std::string& get() & { return data_; }
std::string&& get() && { return std::move(data_); }
const std::string&& get() const&& { return std::move(data_); }
// 4个重载!
};
1.2 Deducing this 基本语法:
C++
class TextBuffer {
std::string data_;
public:
// ✅ C++23:显式对象参数(this 成为普通参数)
// "self" 就是 *this,编译器根据调用者推导类型
// 一个函数覆盖 const / 非const
template <typename Self>
auto&& at(this Self&& self, size_t i) {
// 边界检查...
return std::forward<Self>(self).data_[i];
}
// 一个函数覆盖所有值类别(&, const&, &&, const&&)
template <typename Self>
auto&& get(this Self&& self) {
return std::forward<Self>(self).data_;
}
};
void deducing_this_basic() {
TextBuffer buf;
const TextBuffer cbuf;
buf.at(0); // Self = TextBuffer& → 返回 char&
cbuf.at(0); // Self = const TextBuffer& → 返回 const char&
buf.get(); // Self = TextBuffer& → 返回 string&
cbuf.get(); // Self = const TextBuffer& → 返回 const string&
std::move(buf).get(); // Self = TextBuffer&& → 返回 string&&
}
1.3 简化 CRTP(奇异递归模板模式):
C++
// ❌ C++20 CRTP:基类必须是模板,语法笨重
template <typename Derived>
class Countable_old {
static inline int count_ = 0;
public:
Countable_old() { ++count_; }
static int count() { return count_; }
Derived& self() { return static_cast<Derived&>(*this); }
};
class Widget_old : public Countable_old<Widget_old> {}; // 重复类名
// ✅ C++23:Deducing this 替代 CRTP
class Countable {
static inline int count_ = 0;
public:
Countable() { ++count_; }
static int count() { return count_; }
};
// Builder 模式:不需要 CRTP 就能实现链式调用
struct Builder {
int width_ = 0, height_ = 0;
std::string name_;
// 返回正确的派生类型
template <typename Self>
Self&& set_width(this Self&& self, int w) {
self.width_ = w;
return std::forward<Self>(self);
}
template <typename Self>
Self&& set_height(this Self&& self, int h) {
self.height_ = h;
return std::forward<Self>(self);
}
};
struct WindowBuilder : Builder {
bool fullscreen_ = false;
template <typename Self>
Self&& set_fullscreen(this Self&& self, bool fs) {
self.fullscreen_ = fs;
return std::forward<Self>(self);
}
};
void builder_demo() {
// ✅ 链式调用始终返回 WindowBuilder&,不会退化为 Builder&
auto wb = WindowBuilder{}
.set_width(1920) // 返回 WindowBuilder&&
.set_height(1080) // 返回 WindowBuilder&&
.set_fullscreen(true); // 返回 WindowBuilder&&
}
1.4 递归 Lambda:
C++
void recursive_lambda() {
// ❌ C++20:Lambda 无法直接递归调用自身
// auto fib = [](int n) { return n <= 1 ? n : fib(n-1) + fib(n-2); }; // ❌ fib 未定义
// C++20 workaround:需要额外传入自己
auto fib_old = [](auto self, int n) -> int {
return n <= 1 ? n : self(self, n-1) + self(self, n-2);
};
fib_old(fib_old, 10); // 笨重
// ✅ C++23:使用 deducing this 实现自然递归
auto fib = [](this auto self, int n) -> int {
return n <= 1 ? n : self(n - 1) + self(n - 2);
};
std::cout << fib(10) << "\n"; // 55 ← 直接调用,无需传 self
// 实际应用:树遍历
struct Node {
int value;
std::vector<Node> children;
};
auto print_tree = [](this auto self, const Node& node, int depth = 0) -> void {
std::cout << std::string(depth * 2, ' ') << node.value << "\n";
for (const auto& child : node.children) {
self(child, depth + 1); // ✅ 递归
}
};
}
1.5 Deducing this 总结:
C++
┌──────────────────────────┬──────────────────────────────────────────┐
│ 应用场景 │ 效果 │
├──────────────────────────┼──────────────────────────────────────────┤
│ const / 非const 重载 │ ✅ 一个模板函数替代两个重载 │
├──────────────────────────┼──────────────────────────────────────────┤
│ 4 种值类别重载 │ ✅ 一个完美转发函数替代四个重载 │
├──────────────────────────┼──────────────────────────────────────────┤
│ CRTP │ ✅ 无需模板基类,普通继承即可 │
├──────────────────────────┼──────────────────────────────────────────┤
│ Lambda 递归 │ ✅ 自然递归,不需要 Y-combinator │
├──────────────────────────┼──────────────────────────────────────────┤
│ Builder / 链式调用 │ ✅ 派生类链式调用自动返回正确类型 │
└──────────────────────────┴──────────────────────────────────────────┘
📝 2. std::print / std::println ------ 现代输出
C++
#include <print>
/*
* std::print vs 其他输出方式:
* ┌────────────────┬────────────┬────────────┬────────────┬──────────────┐
* │ │ printf │ cout │ format+cout│ std::print │
* ├────────────────┼────────────┼────────────┼────────────┼──────────────┤
* │ 类型安全 │ ❌ │ ✅ │ ✅ │ ✅ │
* │ 性能 │ ✅ 快 │ ❌ 慢 │ ✅ 快 │ ✅ 最快 │
* │ 可读性 │ ⚠️ 一般 │ ❌ 差 │ ✅ 好 │ ✅ 最好 │
* │ 一步输出 │ ✅ │ ❌ 多步 │ ❌ 两步 │ ✅ │
* │ Unicode │ ⚠️ │ ⚠️ │ ✅ │ ✅ │
* └────────────────┴────────────┴────────────┴────────────┴──────────────┘
*/
void print_examples() {
// ❌ C++20:format + cout 两步
std::cout << std::format("Hello, {}!\n", "World");
// ✅ C++23:一步到位
std::print("Hello, {}!\n", "World");
std::println("Hello, {}!", "World"); // 自动换行
// 格式化参数
std::println("Name: {:>10}, Age: {:03d}", "Alice", 7);
// 输出:Name: Alice, Age: 007
// 多种类型
std::println("{} + {} = {}", 1, 2, 3);
std::println("Pi ≈ {:.4f}", 3.14159);
std::println("Hex: {:#x}, Bin: {:#b}", 255, 42);
// 输出到文件
FILE* fp = fopen("log.txt", "w");
if (fp) {
std::print(fp, "Log entry: {}\n", "something happened");
fclose(fp);
}
// 输出到 stderr
std::print(stderr, "Error: {}\n", "file not found");
// Unicode 支持(正确处理 UTF-8)
std::println("こんにちは、{}!", "世界");
std::println("Emoji: {} {} {}", "🚀", "🔥", "✅");
}
🛡️ 3. std::expected ------ 错误处理的现代方案
C++
#include <expected>
#include <string>
#include <fstream>
/*
* 错误处理方式演进:
* ┌─────────────────────┬─────────────────────────────────────────────┐
* │ 返回错误码 │ 容易忘记检查,返回值语义被占用 │
* │ 异常 │ 性能开销,控制流不明确,不适合频繁错误 │
* │ std::optional │ 只知道"失败了",不知道"为什么失败" │
* │ ✅ std::expected │ 成功时持有值,失败时持有错误信息 │
* └─────────────────────┴─────────────────────────────────────────────┘
*
* std::expected<T, E>:
* - 成功时包含 T 类型的值
* - 失败时包含 E 类型的错误
* - 类似 Rust 的 Result<T, E>
*/
// 错误类型
enum class ParseError {
EmptyInput,
InvalidFormat,
OutOfRange
};
std::string to_string(ParseError e) {
switch (e) {
case ParseError::EmptyInput: return "Empty input";
case ParseError::InvalidFormat: return "Invalid format";
case ParseError::OutOfRange: return "Out of range";
}
return "Unknown";
}
// ✅ 返回 expected:成功返回 int,失败返回 ParseError
std::expected<int, ParseError> parse_int(std::string_view sv) {
if (sv.empty())
return std::unexpected(ParseError::EmptyInput);
try {
int val = std::stoi(std::string(sv));
if (val < 0 || val > 1000)
return std::unexpected(ParseError::OutOfRange);
return val; // ✅ 隐式包装为 expected
} catch (...) {
return std::unexpected(ParseError::InvalidFormat);
}
}
void expected_basic() {
auto r1 = parse_int("42");
if (r1) { // 或 r1.has_value()
std::println("Parsed: {}", *r1); // 42
std::println("Parsed: {}", r1.value()); // 42(无值时抛异常)
}
auto r2 = parse_int("abc");
if (!r2) {
std::println("Error: {}", to_string(r2.error())); // Invalid format
}
// value_or 提供默认值
int val = parse_int("xyz").value_or(-1); // -1
}
// Monadic 操作(链式错误处理)
std::expected<double, ParseError> parse_and_halve(std::string_view sv) {
return parse_int(sv)
.transform([](int v) { return v * 0.5; }); // 成功时变换值
}
std::expected<int, ParseError> parse_positive(std::string_view sv) {
return parse_int(sv)
.and_then([](int v) -> std::expected<int, ParseError> {
if (v <= 0) return std::unexpected(ParseError::OutOfRange);
return v;
});
}
void expected_monadic() {
auto result = parse_int("100")
.transform([](int v) { return v * 2; }) // 200
.transform([](int v) { return std::to_string(v); }) // "200"
.or_else([](ParseError e) -> std::expected<std::string, ParseError> {
return "default"; // 错误时提供替代值
});
std::println("Result: {}", *result); // "200"
}
// 实际应用:文件处理链
enum class FileError { NotFound, PermissionDenied, ParseFailed };
std::expected<std::string, FileError> read_file(const std::string& path) {
std::ifstream file(path);
if (!file) return std::unexpected(FileError::NotFound);
return std::string(std::istreambuf_iterator<char>(file), {});
}
std::expected<int, FileError> read_config_value(const std::string& path) {
return read_file(path)
.and_then([](const std::string& content) -> std::expected<int, FileError> {
try { return std::stoi(content); }
catch (...) { return std::unexpected(FileError::ParseFailed); }
});
}
/*
* expected vs optional vs 异常:
* ┌──────────────────┬───────────────┬───────────────┬──────────────┐
* │ │ optional │ expected │ 异常 │
* ├──────────────────┼───────────────┼───────────────┼──────────────┤
* │ 错误信息 │ ❌ 无 │ ✅ 有 │ ✅ 有 │
* │ 性能 │ ✅ 零开销 │ ✅ 零开销 │ ❌ 抛出时慢 │
* │ 可组合性 │ ✅ monadic │ ✅ monadic │ ⚠️ try/catch│
* │ 强制检查 │ ⚠️ │ ✅ │ ❌ 可忽略 │
* │ 适用场景 │ 可能无值 │ 预期会失败 │ 异常情况 │
* └──────────────────┴───────────────┴───────────────┴──────────────┘
*/
🌊 4. Ranges 大幅增强
4.1 std::ranges::to ------ Range 转容器:
C++
#include <ranges>
#include <vector>
#include <set>
#include <map>
#include <string>
void ranges_to_example() {
namespace views = std::views;
// ❌ C++20:视图转容器需要手动构造
auto view = views::iota(1, 11) | views::filter([](int n) { return n % 2 == 0; });
std::vector<int> v_old(view.begin(), view.end()); // 繁琐
// ✅ C++23:ranges::to 一步转换
auto v = views::iota(1, 11)
| views::filter([](int n) { return n % 2 == 0; })
| std::ranges::to<std::vector>(); // {2, 4, 6, 8, 10}
// 转换为不同容器
auto s = views::iota(1, 11)
| std::ranges::to<std::set>(); // {1,2,...,10} 有序集合
auto d = views::iota(1, 6)
| std::ranges::to<std::deque>(); // deque
// 转换为 string
auto str = views::iota('a', 'f')
| std::ranges::to<std::string>(); // "abcde"
// 嵌套容器
auto nested = views::iota(0, 3)
| views::transform([](int i) {
return views::iota(i * 3, (i + 1) * 3)
| std::ranges::to<std::vector>();
})
| std::ranges::to<std::vector>();
// {{0,1,2}, {3,4,5}, {6,7,8}}
// 带参数的容器构造
auto sorted_vec = views::iota(1, 11)
| views::transform([](int n) { return n * n; })
| std::ranges::to<std::vector<double>>();
}
4.2 新增视图适配器:
C++
#include <ranges>
namespace views = std::views;
void new_views() {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// ═══ views::enumerate ═══ 带索引遍历
for (auto [index, value] : v | views::enumerate) {
std::println("[{}] = {}", index, value);
// [0] = 1, [1] = 2, ...
}
// ═══ views::zip ═══ 将多个范围合并为 tuple
std::vector<std::string> names = {"Alice", "Bob", "Charlie"};
std::vector<int> ages = {30, 25, 35};
for (auto [name, age] : views::zip(names, ages)) {
std::println("{} is {} years old", name, age);
}
// ═══ views::zip_transform ═══ zip + transform
auto sums = views::zip_transform(std::plus{}, v, v);
// {2, 4, 6, 8, ...}(逐元素相加)
// ═══ views::chunk ═══ 分块
for (auto chunk : v | views::chunk(3)) {
for (int x : chunk) std::print("{} ", x);
std::println("|");
}
// 1 2 3 | 4 5 6 | 7 8 9 | 10 |
// ═══ views::slide ═══ 滑动窗口
for (auto window : v | views::slide(3)) {
for (int x : window) std::print("{} ", x);
std::println("|");
}
// 1 2 3 | 2 3 4 | 3 4 5 | ... | 8 9 10 |
// ═══ views::chunk_by ═══ 按条件分组
std::vector<int> data = {1, 1, 2, 2, 2, 3, 1, 1};
for (auto group : data | views::chunk_by(std::equal_to{})) {
for (int x : group) std::print("{} ", x);
std::println("|");
}
// 1 1 | 2 2 2 | 3 | 1 1 |
// ═══ views::stride ═══ 步长(每隔N个取一个)
auto every_third = v | views::stride(3); // {1, 4, 7, 10}
// ═══ views::join_with ═══ 用分隔符连接
std::vector<std::vector<int>> nested = {{1, 2}, {3, 4}, {5, 6}};
auto joined = nested | views::join_with(0);
// {1, 2, 0, 3, 4, 0, 5, 6}
// ═══ views::cartesian_product ═══ 笛卡尔积
std::vector<int> xs = {1, 2, 3};
std::vector<char> ys = {'a', 'b'};
for (auto [x, y] : views::cartesian_product(xs, ys)) {
std::print("({},{}) ", x, y);
}
// (1,a) (1,b) (2,a) (2,b) (3,a) (3,b)
// ═══ views::adjacent / views::pairwise ═══ 相邻元素组
// views::pairwise 等价于 views::adjacent<2>
for (auto [a, b] : v | views::pairwise) {
std::print("({},{}) ", a, b);
}
// (1,2) (2,3) (3,4) ...
// ═══ views::adjacent_transform / views::pairwise_transform ═══
auto diffs = v | views::pairwise_transform(std::minus{});
// {-1, -1, -1, ...}(相邻差)
// ═══ views::repeat ═══ 重复单个值
auto fives = views::repeat(5) | views::take(4); // {5, 5, 5, 5}
auto bounded = views::repeat(42, 3); // {42, 42, 42}
// ═══ views::as_rvalue ═══ 所有元素转为右值
// 用于移动语义场景
std::vector<std::string> src = {"hello", "world"};
auto moved = src | views::as_rvalue; // 遍历时得到 string&&
// ═══ views::as_const ═══ 所有元素转为 const
for (const auto& x : v | views::as_const) {
// x 是 const int&
}
}
4.3 Ranges 折叠算法:
C++
#include <ranges>
#include <algorithm>
#include <functional>
void fold_algorithms() {
std::vector<int> v = {1, 2, 3, 4, 5};
// ❌ C++20:std::accumulate 不在 ranges 命名空间
int sum_old = std::accumulate(v.begin(), v.end(), 0);
// ✅ C++23:ranges::fold_left(替代 accumulate)
auto sum = std::ranges::fold_left(v, 0, std::plus{}); // 15
auto product = std::ranges::fold_left(v, 1, std::multiplies{}); // 120
// fold_right:从右往左折叠
auto right = std::ranges::fold_right(v, 0, std::plus{}); // 15
// fold_left_first:使用第一个元素作为初始值
auto max_val = std::ranges::fold_left_first(v, [](int a, int b) {
return std::max(a, b);
});
// 返回 std::optional<int>,因为范围可能为空
if (max_val) std::println("Max: {}", *max_val); // 5
// 实际应用:字符串拼接
std::vector<std::string> words = {"Hello", " ", "World", "!"};
auto sentence = std::ranges::fold_left(words, std::string{}, std::plus{});
std::println("{}", sentence); // "Hello World!"
// fold_left_with_iter:同时返回结果和迭代器位置
auto [iter, result] = std::ranges::fold_left_with_iter(v, 0, std::plus{});
}
🔄 5. std::generator ------ 标准协程生成器
C++
#include <generator>
#include <ranges>
/*
* C++20 协程的遗憾:只有底层机制,没有开箱即用的生成器类型
* C++23 补齐了 std::generator<T>
*/
// ✅ C++23:标准生成器,不需要手写 promise_type
std::generator<int> fibonacci() {
int a = 0, b = 1;
while (true) {
co_yield a;
auto next = a + b;
a = b;
b = next;
}
}
std::generator<int> range(int start, int end, int step = 1) {
for (int i = start; i < end; i += step) {
co_yield i;
}
}
// 可以与 Ranges 完美结合!
void generator_usage() {
// 取前 10 个斐波那契数
for (int val : fibonacci() | std::views::take(10)) {
std::print("{} ", val);
}
// 0 1 1 2 3 5 8 13 21 34
std::println("");
// 过滤 + 变换
auto even_fibs = fibonacci()
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::take(5);
for (int val : even_fibs) {
std::print("{} ", val); // 0 2 8 34 144
}
std::println("");
// 收集为容器
auto v = range(1, 11) | std::ranges::to<std::vector>();
}
// 递归生成器(使用 co_yield elements_of)
std::generator<int> flatten(const std::vector<std::vector<int>>& nested) {
for (const auto& inner : nested) {
co_yield std::ranges::elements_of(inner); // 递归 yield 整个范围
}
}
// 遍历树结构
struct TreeNode {
int value;
std::vector<TreeNode> children;
};
std::generator<int> traverse(const TreeNode& node) {
co_yield node.value;
for (const auto& child : node.children) {
co_yield std::ranges::elements_of(traverse(child)); // 递归
}
}
void recursive_generator_demo() {
TreeNode tree = {1, {{2, {{4, {}}, {5, {}}}}, {3, {{6, {}}}}}};
for (int val : traverse(tree)) {
std::print("{} ", val); // 1 2 4 5 3 6 (前序遍历)
}
}
📦 6. std::flat_map / std::flat_set
C++
#include <flat_map>
#include <flat_set>
/*
* flat_map/flat_set vs map/set vs unordered_map/unordered_set:
* ┌──────────────────┬──────────┬──────────────┬────────────────┐
* │ │ map/set │ unordered_* │ flat_map/set │
* ├──────────────────┼──────────┼──────────────┼────────────────┤
* │ 底层结构 │ 红黑树 │ 哈希表 │ ✅ 排序数组 │
* │ 查找 │ O(log n) │ 平均 O(1) │ O(log n) │
* │ 插入/删除 │ O(log n) │ 平均 O(1) │ ⚠️ O(n) │
* │ 内存局部性 │ ❌ 差 │ ⚠️ 一般 │ ✅ 极好 │
* │ 缓存友好 │ ❌ │ ⚠️ │ ✅ │
* │ 内存开销 │ 大 │ 大 │ ✅ 小 │
* │ 遍历性能 │ 一般 │ 一般 │ ✅ 极快 │
* └──────────────────┴──────────┴──────────────┴────────────────┘
* 适用场景:数据基本不变,查找和遍历远多于插入删除
*/
void flat_map_example() {
// 基本用法(与 std::map 接口几乎相同)
std::flat_map<std::string, int> scores;
scores["Alice"] = 90;
scores["Bob"] = 85;
scores["Charlie"] = 95;
// 查找
if (auto it = scores.find("Alice"); it != scores.end()) {
std::println("Alice: {}", it->second);
}
// contains
if (scores.contains("Bob")) {
std::println("Bob found!");
}
// 遍历(由于底层是排序数组,遍历极快)
for (const auto& [name, score] : scores) {
std::println("{}: {}", name, score);
}
// 指定底层容器
std::flat_map<int, std::string, std::less<>,
std::vector<int>, std::vector<std::string>> custom_map;
// flat_set
std::flat_set<int> fs = {5, 3, 1, 4, 2};
// 底层是排序的 vector:{1, 2, 3, 4, 5}
// 批量构造(先排序,性能更好)
std::vector<std::pair<std::string, int>> raw_data = {
{"Alice", 90}, {"Bob", 85}, {"Charlie", 95}
};
std::flat_map<std::string, int> from_sorted(
std::sorted_unique, raw_data.begin(), raw_data.end());
}
📊 7. std::mdspan ------ 多维数组视图
C++
#include <mdspan>
/*
* std::mdspan:多维非拥有数组视图
* ┌────────────────────────────────────────────────────────────────┐
* │ span 之于一维数组,mdspan 之于多维数组 │
* │ 不拥有数据,只是对连续内存的多维"观察" │
* │ 支持静态维度和动态维度混合 │
* │ 支持不同布局策略(行优先、列优先、步长等) │
* └────────────────────────────────────────────────────────────────┘
*/
void mdspan_example() {
// 一维数据,解释为 3x4 的二维矩阵
std::vector<int> data(12);
std::iota(data.begin(), data.end(), 1); // {1, 2, ..., 12}
// 创建 3x4 的 mdspan(动态维度)
std::mdspan matrix(data.data(), 3, 4);
// 使用多维下标访问
for (size_t i = 0; i < matrix.extent(0); ++i) {
for (size_t j = 0; j < matrix.extent(1); ++j) {
std::print("{:3d} ", matrix[i, j]); // ✅ C++23 多维下标
}
std::println("");
}
/*
1 2 3 4
5 6 7 8
9 10 11 12
*/
// 静态维度(编译期已知大小)
std::mdspan<int, std::extents<size_t, 3, 4>> fixed_matrix(data.data());
// 混合维度(部分静态、部分动态)
std::mdspan<int, std::extents<size_t, std::dynamic_extent, 4>>
semi_fixed(data.data(), 3); // 行数动态,列数固定为 4
// 查询属性
std::println("Rank: {}", matrix.rank()); // 2
std::println("Rows: {}", matrix.extent(0)); // 3
std::println("Cols: {}", matrix.extent(1)); // 4
std::println("Total: {}", matrix.size()); // 12
}
// 实际应用:矩阵运算
void matrix_multiply(std::mdspan<const double, std::extents<size_t,
std::dynamic_extent, std::dynamic_extent>> A,
std::mdspan<const double, std::extents<size_t,
std::dynamic_extent, std::dynamic_extent>> B,
std::mdspan<double, std::extents<size_t,
std::dynamic_extent, std::dynamic_extent>> C) {
for (size_t i = 0; i < A.extent(0); ++i)
for (size_t j = 0; j < B.extent(1); ++j) {
C[i, j] = 0;
for (size_t k = 0; k < A.extent(1); ++k)
C[i, j] += A[i, k] * B[k, j];
}
}
// 布局策略
void layout_example() {
std::vector<double> data(12);
// 行优先(C 风格,默认)
std::mdspan<double, std::dextents<size_t, 2>, std::layout_right>
row_major(data.data(), 3, 4);
// 列优先(Fortran 风格)
std::mdspan<double, std::dextents<size_t, 2>, std::layout_left>
col_major(data.data(), 3, 4);
// 子视图切片(submdspan)
// auto row0 = std::submdspan(row_major, 0, std::full_extent);
}
🔧 8. std::optional Monadic 操作
C++
#include <optional>
#include <string>
/*
* C++23 为 optional 增加了三个 Monadic 操作:
* ┌───────────────┬───────────────────────────────────────────┐
* │ transform │ 有值时对值应用函数,无值时保持 nullopt │
* │ and_then │ 有值时调用返回 optional 的函数(flatMap) │
* │ or_else │ 无值时执行替代操作 │
* └───────────────┴───────────────────────────────────────────┘
*/
std::optional<std::string> get_env(const std::string& name) {
const char* val = std::getenv(name.c_str());
if (val) return std::string(val);
return std::nullopt;
}
std::optional<int> parse_int(const std::string& s) {
try { return std::stoi(s); }
catch (...) { return std::nullopt; }
}
void optional_monadic() {
// ❌ C++20:嵌套 if 检查
auto env = get_env("PORT");
int port_old = 80;
if (env) {
auto parsed = parse_int(*env);
if (parsed) {
port_old = *parsed;
}
}
// ✅ C++23:链式 Monadic 操作
int port = get_env("PORT") // optional<string>
.and_then(parse_int) // optional<int>(flatMap)
.transform([](int p) { return p + 1000; }) // optional<int>(map)
.or_else([]() -> std::optional<int> { // 无值时的替代
std::println("Using default port");
return 8080;
})
.value(); // 提取值
// transform:有值时变换
std::optional<std::string> name = "Alice";
auto greeting = name.transform([](const std::string& n) {
return "Hello, " + n + "!";
});
// greeting = optional<string>{"Hello, Alice!"}
std::optional<std::string> empty;
auto no_greeting = empty.transform([](const std::string& n) {
return "Hello, " + n + "!";
});
// no_greeting = nullopt(不会调用 Lambda)
// and_then:类似 transform 但函数返回 optional(防止嵌套 optional)
auto result = get_env("HOME")
.and_then([](const std::string& home) -> std::optional<std::string> {
if (home.starts_with("/")) return home + "/.config";
return std::nullopt;
});
// or_else:无值时的恢复/日志
auto value = get_env("MISSING")
.or_else([]() -> std::optional<std::string> {
std::println(stderr, "Warning: MISSING not set");
return "fallback";
});
}
🏗️ 9. 语言特性增强
9.1 if consteval:
C++
// ❌ C++20:std::is_constant_evaluated() 需要 <type_traits>,且有陷阱
constexpr int compute_20(int x) {
if (std::is_constant_evaluated()) { // ⚠️ if constexpr 不行,必须是 if
return x * x; // 编译期路径
} else {
return x * x; // 运行期路径(可能用 SIMD 等)
}
}
// ✅ C++23:if consteval ------ 直观、安全、无需 include
constexpr int compute_23(int x) {
if consteval {
// 编译期路径 ------ 可以调用 consteval 函数
return x * x;
} else {
// 运行期路径 ------ 可以调用非 constexpr 函数
return x * x;
}
}
// if !consteval 也支持
constexpr void log_value(int x) {
if !consteval {
// 仅在运行期执行
std::println("Runtime value: {}", x);
}
// 编译期什么都不做
}
9.2 多维下标运算符 operator[]:
C++
// ✅ C++23:operator[] 可以接受多个参数
template <typename T>
class Matrix {
std::vector<T> data_;
size_t rows_, cols_;
public:
Matrix(size_t r, size_t c) : data_(r * c), rows_(r), cols_(c) {}
// ✅ 多维下标
T& operator[](size_t i, size_t j) {
return data_[i * cols_ + j];
}
const T& operator[](size_t i, size_t j) const {
return data_[i * cols_ + j];
}
};
void multidim_subscript() {
Matrix<double> m(3, 4);
// ❌ C++20:需要用 operator()(i, j) 或 m[i][j](代理对象)
// ✅ C++23:自然的多维下标
m[0, 0] = 1.0;
m[1, 2] = 3.14;
m[2, 3] = 42.0;
std::println("m[1,2] = {}", m[1, 2]); // 3.14
}
// 三维数组
template <typename T>
class Tensor {
std::vector<T> data_;
size_t d1_, d2_, d3_;
public:
T& operator[](size_t i, size_t j, size_t k) {
return data_[i * d2_ * d3_ + j * d3_ + k];
}
};
9.3 static operator() 与 static operator[]:
C++
// ✅ C++23:operator() 和 operator[] 可以是 static
// 无状态函数对象不需要 this 指针,消除间接调用开销
struct Add {
static int operator()(int a, int b) { // ✅ static operator()
return a + b;
}
};
struct ConstantLookup {
static constexpr int table[] = {0, 1, 4, 9, 16, 25};
static int operator[](size_t i) { // ✅ static operator[]
return table[i];
}
};
// 无状态 Lambda 也可以是 static(编译器可自动优化)
auto multiply = [](static int a, int b) { return a * b; };
// 注意:实际语法是 Lambda 被标记为 static
auto add = []static(int a, int b) { return a + b; }; // 无捕获时可标 static
void static_operator_demo() {
Add::operator()(1, 2); // ✅ 无需实例
ConstantLookup::operator[](3); // ✅ 无需实例
// 仍然可以通过实例调用
Add adder;
adder(1, 2); // ✅
}
9.4 auto(x) / auto{x} ------ 显式衰减拷贝:
C++
// ✅ C++23:auto(x) 创建 x 的衰减(decay)拷贝
void decay_copy() {
int arr[] = {1, 2, 3};
// auto(arr) → int*(数组衰减为指针的拷贝)
auto ptr = auto(arr); // int*
const int& ref = arr[0];
auto val = auto(ref); // int(去掉引用和 const)
// 最重要的用途:在需要临时值的地方显式创建拷贝
std::string s = "hello";
// 创建一个临时拷贝传递(而非引用)
auto process = [](std::string s) { /* ... */ };
process(auto(s)); // 明确表示"传一份拷贝"
// 在 Ranges 管道中使用
std::vector<std::string> strings = {"hello", "world"};
// auto(x) 确保每次得到一份拷贝
auto copies = strings | std::views::transform([](auto& s) { return auto(s); });
}
9.5 [[assume(expr)]]:
C++
// ✅ C++23:向编译器断言某个条件为真(帮助优化)
// 不同于 assert(运行期),assume 仅用于编译期优化提示
int fast_divide(int x, int y) {
[[assume(y > 0)]]; // 告诉编译器 y 一定 > 0
[[assume(x >= 0)]]; // 告诉编译器 x 一定 >= 0
return x / y; // 编译器可以省去符号检查等
}
void process_data(int* data, size_t size) {
[[assume(size > 0)]]; // 数据非空
[[assume(size % 4 == 0)]]; // 大小是 4 的倍数(利于向量化)
[[assume((size & (size - 1)) == 0)]]; // 大小是 2 的幂
for (size_t i = 0; i < size; ++i) {
data[i] *= 2;
}
}
/*
* ⚠️ 注意事项:
* - 如果 assume 的条件在运行时不成立 → 未定义行为!
* - assume 不会被求值(没有运行期开销)
* - 仅用于性能关键路径中编译器无法自行推断的条件
* - 不要用 assume 替代 assert(assert 用于调试,assume 用于优化)
*/
9.6 size_t 字面量后缀:
C++
void size_literals() {
// ❌ C++20:容易发生有符号/无符号比较警告
for (int i = 0; i < vec.size(); ++i) { // ⚠️ 有符号/无符号比较
// ...
}
// ✅ C++23:uz / UZ 后缀表示 size_t
auto s = 42uz; // std::size_t
auto s2 = 42UZ; // std::size_t
// 有符号 size 类型
auto ss = 42z; // std::make_signed_t<std::size_t>(通常是 ptrdiff_t)
auto ss2 = 42Z;
for (auto i = 0uz; i < vec.size(); ++i) { // ✅ 无警告
// ...
}
// 在范围构造中消除警告
auto v = std::views::iota(0uz, vec.size());
}
9.7 其他语言改进:
C++
// ═══ #warning 预处理指令(标准化)═══
#warning "This feature is experimental" // ✅ 之前是扩展,C++23 标准化
// ═══ #elifdef / #elifndef ═══
#ifdef FEATURE_A
// ...
#elifdef FEATURE_B // ✅ 之前需要 #elif defined(FEATURE_B)
// ...
#elifndef FEATURE_C
// ...
#endif
// ═══ constexpr 进一步放宽 ═══
// C++23 允许 constexpr 函数中使用:
// - static 变量(受限)
// - goto(受限)
// - 非字面量类型的变量(受限条件下)
// - constexpr 函数中的 labels
// ═══ 标记语句可以出现在复合语句末尾 ═══
void example() {
goto end;
// ...
end: // ✅ C++23 允许标签在 } 前(C++20 不允许空标签)
}
📚 10. 标准库其他增强
10.1 std::stacktrace ------ 运行期堆栈跟踪:
C++
#include <stacktrace>
void inner_function() {
// ✅ 捕获当前堆栈跟踪
auto trace = std::stacktrace::current();
std::println("Stack trace:");
for (const auto& entry : trace) {
std::println(" {} at {}:{}",
entry.description(),
entry.source_file(),
entry.source_line());
}
// 或直接打印
std::println("{}", std::to_string(trace));
}
void outer_function() {
inner_function();
}
void stacktrace_demo() {
outer_function();
/*
可能输出:
Stack trace:
inner_function() at main.cpp:5
outer_function() at main.cpp:15
stacktrace_demo() at main.cpp:19
main at main.cpp:25
*/
}
// 实际应用:增强版异常
class TracedException : public std::exception {
std::string message_;
std::stacktrace trace_;
public:
TracedException(const std::string& msg)
: message_(msg), trace_(std::stacktrace::current()) {}
const char* what() const noexcept override { return message_.c_str(); }
const std::stacktrace& trace() const { return trace_; }
};
10.2 std::move_only_function:
C++
#include <functional>
/*
* std::function vs std::move_only_function:
* ┌────────────────────────┬──────────────┬──────────────────────┐
* │ │ function │ move_only_function │
* ├────────────────────────┼──────────────┼──────────────────────┤
* │ 可拷贝 │ ✅ │ ❌ 只能移动 │
* │ 存储 move-only 对象 │ ❌ │ ✅ │
* │ const 限定 │ ❌ 总是非const│ ✅ 可指定 │
* │ noexcept 限定 │ ❌ │ ✅ 可指定 │
* │ 开销 │ 稍高(需拷贝)│ ✅ 更低 │
* └────────────────────────┴──────────────┴──────────────────────┘
*/
void move_only_function_example() {
// ❌ std::function 不能存储 move-only 的可调用对象
auto ptr = std::make_unique<int>(42);
// std::function<void()> f = [p = std::move(ptr)]() { std::println("{}", *p); };
// ❌ 编译错误:unique_ptr 不可拷贝
// ✅ std::move_only_function 可以
auto ptr2 = std::make_unique<int>(42);
std::move_only_function<void()> f = [p = std::move(ptr2)]() {
std::println("{}", *p);
};
f(); // 42
// 带 const / noexcept 限定
std::move_only_function<int(int) const> pure_fn = [](int x) { return x * 2; };
std::move_only_function<void() noexcept> safe_fn = []() noexcept {};
// 实际应用:任务队列(任务通常只需要执行一次)
std::vector<std::move_only_function<void()>> tasks;
tasks.push_back([data = std::make_unique<int>(1)]() {
std::println("Task with data: {}", *data);
});
for (auto& task : tasks) {
task(); // 执行任务
}
}
10.3 std::to_underlying 与 std::unreachable:
C++
#include <utility>
// std::to_underlying:枚举值 → 底层整数类型
enum class Color : uint8_t { Red = 1, Green = 2, Blue = 4 };
void to_underlying_example() {
// ❌ C++20
auto val = static_cast<std::underlying_type_t<Color>>(Color::Red);
// ✅ C++23
auto val2 = std::to_underlying(Color::Red); // uint8_t{1}
std::println("Color value: {}", val2);
// 位运算更方便
auto mask = std::to_underlying(Color::Red) | std::to_underlying(Color::Blue);
}
// std::unreachable:标记不可达代码
int process(int x) {
switch (x) {
case 1: return 10;
case 2: return 20;
case 3: return 30;
}
// ✅ 告诉编译器此处永远不会执行到
std::unreachable();
// 编译器可据此进行更激进的优化
// 如果真的执行到这里 → 未定义行为
}
// 实际应用
enum class Direction { North, South, East, West };
std::string_view to_string(Direction d) {
switch (d) {
case Direction::North: return "North";
case Direction::South: return "South";
case Direction::East: return "East";
case Direction::West: return "West";
}
std::unreachable(); // 所有情况已覆盖,这里不可达
}
10.4 std::byteswap:
C++
#include <bit>
void byteswap_example() {
// ✅ C++23:字节序反转(大小端转换)
uint32_t value = 0x01020304;
uint32_t swapped = std::byteswap(value); // 0x04030201
uint16_t port = 0x1F90; // 8080 in big-endian
uint16_t host_port = std::byteswap(port); // 转为主机字节序
// 网络编程中替代 htonl/ntohl
uint32_t ip = 0xC0A80001; // 192.168.0.1
uint32_t network_ip = std::byteswap(ip);
std::println("0x{:08X} → 0x{:08X}", value, swapped);
}
10.5 字符串增强:
C++
void string_enhancements() {
// ═══ std::string::contains(C++23)═══
std::string text = "Hello, World!";
// ❌ C++20
if (text.find("World") != std::string::npos) { /* ... */ }
// ✅ C++23
if (text.contains("World")) {
std::println("Found 'World'!");
}
if (text.contains('!')) {
std::println("Has exclamation!");
}
// ═══ std::string::resize_and_overwrite ═══
// 高效构建字符串(避免不必要的初始化)
std::string s;
s.resize_and_overwrite(100, [](char* buf, size_t count) -> size_t {
// 直接写入缓冲区
int written = std::sprintf(buf, "Value: %d", 42);
return written; // 返回实际写入的长度
});
// s = "Value: 42",只分配一次,无多余初始化
}
10.6 std::out_ptr / std::inout_ptr:
C++
#include <memory>
// 与 C API(输出参数为指针的指针)安全交互
// 模拟 C API
extern "C" {
int create_resource(int** out) {
*out = new int(42);
return 0; // 成功
}
void destroy_resource(int* p) {
delete p;
}
int resize_resource(int** inout, int new_val) {
delete *inout;
*inout = new int(new_val);
return 0;
}
}
void out_ptr_example() {
// ❌ 传统做法:需要手动管理裸指针
int* raw = nullptr;
create_resource(&raw);
std::unique_ptr<int, decltype(&destroy_resource)> p(raw, destroy_resource);
// ✅ C++23:std::out_ptr 安全桥接
std::unique_ptr<int, decltype(&destroy_resource)>
smart(nullptr, destroy_resource);
create_resource(std::out_ptr(smart));
// out_ptr 自动将结果存入 smart
std::println("Value: {}", *smart); // 42
// std::inout_ptr:已有资源需要更新
resize_resource(std::inout_ptr(smart), 100);
// inout_ptr 先释放旧资源,再接收新资源
std::println("New value: {}", *smart); // 100
}
10.7 std::invoke_r:
C++
#include <functional>
// std::invoke_r<R>:调用可调用对象并指定返回类型
void invoke_r_example() {
auto lambda = [](int x) { return x * 2; };
// std::invoke 返回 int
auto result = std::invoke(lambda, 21); // int: 42
// std::invoke_r 返回指定类型
auto d = std::invoke_r<double>(lambda, 21); // double: 42.0
// 主要用途:void 返回类型(丢弃返回值)
std::invoke_r<void>(lambda, 21); // 调用但忽略返回值
}
📊 11. 综合特性速查表
C++
┌──────────────────────┬───────────────────────────────────────────────────────┐
│ 特性分类 │ 具体特性 │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 核心语言 │ │
│ Deducing this │ 显式对象参数 (this auto self, ...) │
│ │ 消除 const/非const 重载、简化 CRTP、Lambda 递归 │
│ if consteval │ 替代 is_constant_evaluated() │
│ 多维下标 │ operator[](int, int) 多参数下标 │
│ static operator │ static operator(), static operator[] │
│ auto(x) │ 显式衰减拷贝 │
│ [[assume]] │ 编译器优化提示 │
│ size_t 字面量 │ 42uz, 42z │
│ #warning │ 标准化警告预处理指令 │
│ #elifdef │ 简化条件编译 │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 输出 │ std::print / std::println │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 错误处理 │ std::expected<T, E>(值或错误)+ Monadic 操作 │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ Ranges 增强 │ ranges::to(转容器) │
│ │ views::enumerate, zip, chunk, slide, stride │
│ │ views::chunk_by, join_with, cartesian_product │
│ │ views::pairwise, adjacent, repeat, as_rvalue │
│ │ fold_left / fold_right / fold_left_first │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 协程 │ std::generator<T>(标准生成器) │
│ │ co_yield ranges::elements_of(递归生成) │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 容器 │ std::flat_map / std::flat_set(排序数组实现) │
│ │ std::mdspan(多维数组视图) │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ optional 增强 │ transform, and_then, or_else(Monadic 操作) │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 调试/诊断 │ std::stacktrace(运行期堆栈跟踪) │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 函数对象 │ std::move_only_function(只移动的 function) │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 工具 │ std::to_underlying, std::unreachable │
│ │ std::byteswap, std::out_ptr / std::inout_ptr │
│ │ std::invoke_r │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 字符串 │ std::string::contains │
│ │ std::string::resize_and_overwrite │
└──────────────────────┴───────────────────────────────────────────────────────┘
💡 与 C++20 的演进关系
┌─────────────────────────────────────────────────────────────────────────┐
│ C++20 → C++23 演进关系 │
├───────────────────────────┬─────────────────────────────────────────────┤
│ C++20 的痛点/缺失 │ C++23 的改进 │
├───────────────────────────┼─────────────────────────────────────────────┤
│ const/非const 重载重复代码 │ ✅ Deducing this 一个函数覆盖所有 │
│ CRTP 写法笨重 │ ✅ Deducing this 替代 CRTP │
│ Lambda 不能递归 │ ✅ [](this auto self, ...) 自然递归 │
├───────────────────────────┼─────────────────────────────────────────────┤
│ format + cout 两步输出 │ ✅ std::print / println 一步到位 │
├───────────────────────────┼─────────────────────────────────────────────┤
│ optional 无错误信息 │ ✅ std::expected<T, E> 携带错误 │
│ optional 无链式操作 │ ✅ transform / and_then / or_else │
├───────────────────────────┼─────────────────────────────────────────────┤
│ 协程无标准生成器 │ ✅ std::generator 开箱即用 │
├───────────────────────────┼─────────────────────────────────────────────┤
│ Ranges 视图转容器困难 │ ✅ ranges::to<vector>() 一步收集 │
│ 缺少 zip/enumerate/chunk │ ✅ 大量新视图适配器 │
│ 无 fold 算法 │ ✅ fold_left / fold_right │
├───────────────────────────┼─────────────────────────────────────────────┤
│ 无缓存友好的有序容器 │ ✅ flat_map / flat_set │
│ 无多维数组抽象 │ ✅ std::mdspan │
├───────────────────────────┼─────────────────────────────────────────────┤
│ function 不能存 move-only │ ✅ move_only_function │
├───────────────────────────┼─────────────────────────────────────────────┤
│ is_constant_evaluated 笨拙│ ✅ if consteval 直观语法 │
├───────────────────────────┼─────────────────────────────────────────────┤
│ 无标准堆栈跟踪 │ ✅ std::stacktrace │
├───────────────────────────┼─────────────────────────────────────────────┤
│ string 不能直接 contains │ ✅ string::contains() │
├───────────────────────────┼─────────────────────────────────────────────┤
│ enum 转整数写法冗长 │ ✅ std::to_underlying │
├───────────────────────────┼─────────────────────────────────────────────┤
│ 与 C API 指针交互不安全 │ ✅ std::out_ptr / std::inout_ptr │
├───────────────────────────┼─────────────────────────────────────────────┤
│ size_t 字面量缺失致警告 │ ✅ 42uz 后缀 │
└───────────────────────────┴─────────────────────────────────────────────┘
🔭 C++ 标准演进全景图
C++
┌─────────┬─────────────────────────────────────────────────────────┐
│ 版本 │ 定位与核心主题 │
├─────────┼─────────────────────────────────────────────────────────┤
│ C++11 │ 🏗️ "现代 C++ 的奠基" │
│ │ 移动语义、智能指针、Lambda、auto、线程、右值引用 │
├─────────┼─────────────────────────────────────────────────────────┤
│ C++14 │ 🔧 "C++11 的打磨" │
│ │ 泛型 Lambda、放宽 constexpr、make_unique、decltype(auto)│
├─────────┼─────────────────────────────────────────────────────────┤
│ C++17 │ 📦 "补齐短板" │
│ │ 结构化绑定、optional/variant/any、filesystem、 │
│ │ string_view、并行算法、if constexpr │
├─────────┼─────────────────────────────────────────────────────────┤
│ C++20 │ 🚀 "第二次革命" │
│ │ Concepts、Ranges、Coroutines、Modules、 │
│ │ <=>、constexpr 扩展、format、span、jthread │
├─────────┼─────────────────────────────────────────────────────────┤
│ C++23 │ ✨ "C++20 的完善" │
│ │ Deducing this、print、expected、generator、 │
│ │ Ranges 大量新视图、flat_map、mdspan、stacktrace │
├─────────┼─────────────────────────────────────────────────────────┤
│ C++26 │ 🔮 "下一个大版本"(制定中) │
│ │ Contracts、Reflection、Senders/Receivers、 │
│ │ std::execution、Pattern Matching(可能) │
└─────────┴─────────────────────────────────────────────────────────┘
💡 关键实践原则
- 用 Deducing this 消除 const/非const 重载
- 一个模板成员函数替代两个甚至四个重载
- 替代 CRTP 实现链式调用和多态行为
- Lambda 递归不再需要 workaround
- 用
std::expected替代异常或错误码进行常规错误处理- 零开销、类型安全、强制检查
- Monadic 链式操作让错误处理优雅简洁
- 异常仍用于真正的"异常"情况,expected 用于"预期中的失败"
- 用
std::print/std::println替代cout和printf- 类型安全 + 高性能 + 正确的 Unicode 处理
- 告别
std::cout << x << " " << y << "\n"的链式噩梦
- 用
ranges::to和新视图完善 Ranges 管道| ranges::to<vector>()解决了"最后一公里"问题views::enumerate/views::zip/views::chunk极大丰富表达力fold_left取代accumulate成为 Ranges 原生折叠
- 用
std::generator替代手写的协程基础设施- 标准库开箱即用的生成器,无需自定义 promise_type
- 与 Ranges 无缝组合,惰性求值
- 用
std::flat_map/std::flat_set提升缓存性能- 数据构建完成后查找/遍历远多于插入时,性能优于
std::map - 内存紧凑,缓存友好
- 数据构建完成后查找/遍历远多于插入时,性能优于
- 善用
optional的 Monadic 操作替代嵌套 ifand_then/transform/or_else让链式处理更函数式expected的同名操作同理
- 用
std::stacktrace增强调试和错误报告- 运行期获取堆栈信息,用于日志和异常诊断
- 替代平台特定的 backtrace API
总结:
C++23 是 C++20 的"黄金补丁",它没有引入全新的编程范式,但通过大量精心设计的改进,让 C++20 引入的新特性真正可用、好用。Deducing this 被广泛认为是 C++23 最重要的语言特性,它以极其优雅的方式解决了困扰 C++ 开发者数十年的重载膨胀和 CRTP 复杂性问题。std::expected 为 C++ 的错误处理提供了与 Rust Result 类似的零开销方案。std::print 终于让 C++ 有了现代语言标配的格式化输出。std::generator 让 C++20 协程从"有引擎但没有方向盘"变为"开箱即用"。
Ranges 库的大幅扩展------ranges::to、enumerate、zip、chunk、slide、cartesian_product 以及 fold 算法------使其从 C++20 的"概念验证"成长为真正可以替代手写循环的完整数据处理框架。flat_map/flat_set 和 mdspan 则面向性能关键场景提供了缓存友好的数据结构。
从 C++11 到 C++23,现代 C++ 已经走过了一段令人惊叹的演进之路。每一个版本都在"让正确的代码更容易写,让错误的代码更难写"的方向上迈进一大步。对于开发者而言,持续跟进标准演进、逐步采用新特性,是提升代码质量和开发效率的最有效途径。