前置知识: C++

C++23与C++26新特性

00:00
4 min Intermediate 2026/6/14

最新C++标准特性

概述

C++ 标准每三年发布一个新版本。C++23 于 2023 年定稿,带来了 std::expected、std::print、std::flat_map 等实用特性;C++26 正在制定中,预计引入反射、契约、线性代数库等重大特性。了解这些新特性可以让你写出更简洁、更安全、更高效的代码。

为什么需要关注新特性?C++ 的新特性通常是为了解决现有语法的痛点。std::expected 让错误处理不再依赖异常,std::print 让控制台输出不再需要 iostream 的繁琐,flat_map 提供了更高效的关联容器。掌握新特性能让你的代码更现代、更高效。

基础概念

std::expected:一种可以包含期望值或错误的对象,是 std::optional 的推广。适合替代异常进行错误处理。

std::print:类型安全的格式化输出,类似 Python 的 print 和 Rust 的 println。替代 std::cout 的繁琐语法。

std::flat_map / std::flat_set:基于有序数组关联容器,于小数据集比 std::map 更高效(缓存友好)。

std::generator协程生成器,用于惰性生成序列数据。

deducing this允许成员函数根据对象表达方式(左值/右值const/non-const)自动推导 this类型

快速上手

std::print(C++23)

#include <print>

int main() {
    // 基本输出
    std::print("你好,世界!\n");

    // 格式化输出
    std::string name = "张三";
    int age = 25;
    double score = 95.5;

    std::print("姓名: {}, 年龄: {}, 成绩: {:.1f}\n", name, age, score);

    // 带位置参数的格式化
    std::print("{0} 今年 {1} 岁,{0} 的成绩是 {2:.1f}\n", name, age, score);

    // 输出到标准错误
    std::println(stderr, "错误信息");

    // println 自动添加换行
    std::println("不需要手动加换行符");
}

std::expected(C++23)

#include <expected>
#include <string>
#include <iostream>

// 定义错误类型
enum class ParseError {
    EmptyInput,
    InvalidCharacter,
    OutOfRange
};

// 返回 expected:成功时包含 int,失败时包含 ParseError
std::expected<int, ParseError> parseInteger(const std::string& str) {
    if (str.empty()) {
        return std::unexpected(ParseError::EmptyInput);
    }

    // 检查是否都是数字
    for (char c : str) {
        if (!std::isdigit(c)) {
            return std::unexpected(ParseError::InvalidCharacter);
        }
    }

    try {
        int value = std::stoi(str);
        return value;
    } catch (...) {
        return std::unexpected(ParseError::OutOfRange);
    }
}

int main() {
    auto result = parseInteger("42");

    if (result.has_value()) {
        std::println("解析成功: {}", result.value());
    } else {
        switch (result.error()) {
            case ParseError::EmptyInput:
                std::println("错误: 输入为空");
                break;
            case ParseError::InvalidCharacter:
                std::println("错误: 包含非数字字符");
                break;
            case ParseError::OutOfRange:
                std::println("错误: 数值超出范围");
                break;
        }
    }
}

详细用法

std::flat_map 和 std::flat_set(C++23)

#include <flat_map>
#include <flat_set>
#include <string>
#include <iostream>

void flatContainerDemo() {
    // flat_map:基于有序数组的 map
    // 对于小数据集,比 std::map 更快(缓存友好、无节点分配开销)
    std::flat_map<std::string, int> scores;

    scores["张三"] = 95;
    scores["李四"] = 87;
    scores["王五"] = 92;

    // 遍历(按键排序)
    for (const auto& [name, score] : scores) {
        std::println("{}: {}", name, score);
    }

    // 查找
    if (auto it = scores.find("张三"); it != scores.end()) {
        std::println("找到: {}", it->second);
    }

    // flat_set:基于有序数组的 set
    std::flat_set<int> uniqueNumbers = {5, 3, 1, 4, 2, 3, 1};
    // 自动排序并去重: {1, 2, 3, 4, 5}

    // 何时使用 flat_map 而非 std::map?
    // - 数据量小(通常少于 100 个元素)
    // - 读取多,插入/删除少
    // - 需要更好的缓存性能
}

std::generator(C++23)

#include <generator>
#include <iostream>

// 生成斐波那契数列
std::generator<int> fibonacci() {
    int a = 0, b = 1;
    while (true) {
        co_yield a;        // 惰性生成一个值
        int temp = a + b;
        a = b;
        b = temp;
    }
}

// 生成范围内的整数
std::generator<int> range(int start, int end) {
    for (int i = start; i < end; i++) {
        co_yield i;
    }
}

// 过滤生成器
std::generator<int> evenNumbers(std::generator<int> source) {
    for (int value : source) {
        if (value % 2 == 0) {
            co_yield value;
        }
    }
}

int main() {
    // 使用生成器
    for (int value : fibonacci()) {
        if (value > 100) break;
        std::println("{}", value);
    }

    // 组合生成器
    for (int value : evenNumbers(range(0, 20))) {
        std::println("偶数: {}", value);
    }
}

std::expected 链式操作

#include <expected>
#include <string>
#include <fstream>

// 文件操作可能失败
std::expected<std::string, std::string> readFile(const std::string& path) {
    std::ifstream file(path);
    if (!file.is_open()) {
        return std::unexpected("无法打开文件: " + path);
    }
    std::string content((std::istreambuf_iterator<char>(file)),
                         std::istreambuf_iterator<char>());
    return content;
}

// 解析可能失败
std::expected<int, std::string> parseConfig(const std::string& content) {
    if (content.empty()) {
        return std::unexpected("配置内容为空");
    }
    // 简化:返回内容长度作为配置值
    return static_cast<int>(content.size());
}

// 链式处理
void processConfig(const std::string& configPath) {
    auto result = readFile(configPath)
        .and_then([](const std::string& content) {
            return parseConfig(content);
        })
        .transform([](int value) {
            return value * 2;
        });

    if (result.has_value()) {
        std::println("处理结果: {}", result.value());
    } else {
        std::println("错误: {}", result.error());
    }
}

if consteval(C++23)

#include <cmath>

// consteval 函数:必须在编译期执行
consteval int square(int n) {
    return n * n;
}

// if consteval:判断当前是否在编译期执行
constexpr double compute(double x) {
    if consteval {
        // 编译期路径:只能使用 constexpr 函数
        return x * x;
    } else {
        // 运行期路径:可以使用任何函数
        return std::pow(x, 2);
    }
}

int main() {
    // 编译期计算
    constexpr auto a = compute(3.0);  // 走 consteval 分支

    // 运行期计算
    double x = 3.0;
    auto b = compute(x);  // 走 else 分支

    return 0;
}

多维下标运算符(C++23)

#include <vector>
#include <cassert>

// 二维矩阵类
class Matrix {
    std::vector<double> data_;
    size_t rows_, cols_;

public:
    Matrix(size_t rows, size_t cols)
        : data_(rows * cols), rows_(rows), cols_(cols) {}

    // C++23:多维下标运算符
    double& operator[](size_t row, size_t col) {
        return data_[row * cols_ + col];
    }

    const double& operator[](size_t row, size_t col) const {
        return data_[row * cols_ + col];
    }
};

int main() {
    Matrix mat(3, 4);

    // C++23 语法:直接使用多维下标
    mat[1, 2] = 3.14;

    // 之前需要用函数调用
    // mat(1, 2) = 3.14;  或  mat.at(1, 2) = 3.14;

    return 0;
}

std::string 的改进(C++23)

#include <string>
#include <iostream>

void stringImprovements() {
    std::string text = "Hello, World!";

    // contains:检查是否包含子串(C++23 之前需要用 find)
    if (text.contains("World")) {
        std::println("包含 'World'");
    }

    // starts_with / ends_with(C++20 引入,C++23 完善支持)
    if (text.starts_with("Hello")) {
        std::println("以 'Hello' 开头");
    }
    if (text.ends_with("!")) {
        std::println("以 '!' 结尾");
    }
}

C++26 预览特性

// 1. 契约(Contracts)- C++26 预计引入
// 允许在函数上添加前置条件、后置条件和断言

// 预计语法(具体语法可能变化):
// int divide(int a, int b)
//     [[pre: b != 0]]           // 前置条件
//     [[post r: r == a / b]]    // 后置条件
// {
//     return a / b;
// }

// 2. 反射(Reflection)- C++26 可能引入
// 允许在编译期检查和操作类型信息
// 类似 C# 的反射但在编译期执行

// 3. 线性代数库 std::linalg
// 基于 BLAS 的线性代数操作
#include <linalg>  // C++26 预计

// 矩阵乘法等操作将标准化
// std::linalg::matrix_multiply(A, B, C);

// 4. 调试工具 std::is_debugger_present
// 检查是否在调试器中运行
// if (std::is_debugger_present()) { ... }

// 5. hazard_pointer 和 rcu
// 无锁数据结构的标准化支持

常见场景

使用 std::expected 替代异常

#include <expected>
#include <string>
#include <vector>

// 定义统一的错误类型
struct Error {
    int code;
    std::string message;
};

// 数据库操作
std::expected<std::vector<User>, Error> queryUsers(const std::string& sql) {
    if (sql.empty()) {
        return std::unexpected(Error{400, "SQL 不能为空"});
    }

    // 模拟数据库查询
    if (!isConnected()) {
        return std::unexpected(Error{503, "数据库未连接"});
    }

    std::vector<User> users = executeQuery(sql);
    return users;
}

// 使用
void displayUsers() {
    auto result = queryUsers("SELECT * FROM users");
    if (result.has_value()) {
        for (const auto& user : result.value()) {
            std::println("{}", user.name);
        }
    } else {
        std::println("错误 [{}]: {}", result.error().code, result.error().message);
    }
}

注意事项

编译器支持:C++23 和 C++26 的特性需要较新的编译器版本。GCC 13+、Clang 16+、MSVC 19.34+ 支持部分 C++23 特性

渐进采用:不需要一次性迁移所有代码到新特性。可以在新代码中使用新特性,旧代码保持不变。

std::expected异常选择expected 适合可预期的错误(如文件存在错误),异常适合不可预期的错误(如内存不足)。两者可以共存。

flat_map 的适用场景:flat_map 在插入删除时需要移动元素,时间复杂度为 O(n)。不适合频繁插入删除的大数据集。

C++26 特性稳定:C++26 的特性仍在制定中,具体语法行为可能变化。不建议生产代码中使用未定稿的特性

进阶用法

deducing this(C++23)

// deducing this:成员函数根据对象的值类别自动推导
struct Widget {
    int value = 0;

    // 传统写法:需要四个重载
    // int& get() { return value; }
    // const int& get() const { return value; }
    // int&& get() && { return std::move(value); }
    // const int&& get() const && { return std::move(value); }

    // C++23:一个函数搞定
    template<typename Self>
    auto&& get(this Self&& self) {
        return std::forward<Self>(self).value;
    }
};

int main() {
    Widget w;
    w.get();            // 返回 int&

    const Widget cw;
    cw.get();           // 返回 const int&

    Widget().get();     // 返回 int&&
}

std::unreachable(C++23)

#include <utility>

enum class Color { Red, Green, Blue };

std::string_view colorName(Color c) {
    switch (c) {
        case Color::Red:   return "红色";
        case Color::Green: return "绿色";
        case Color::Blue:  return "蓝色";
    }
    // 告诉编译器这个位置不可达
    // 如果运行到这里,行为未定义(但编译器可以优化)
    std::unreachable();
}

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式