C++26 与最新标准
C++11 到 C++26 的标准演化主线——各版本核心特性、演进脉络与取舍;机制细节(虚函数表/RAII/模板元编程)与工具链(CMake/vcpkg)见各自专篇。
前置知识
- C++ 概述与现代标准:知道各标准版本号与编译器 -std 开关;
- 本篇是「标准演化时间线」本位:按版本梳理语言与库特性的演进与取舍。
学习目标
- 掌握「1. C++26 与正在演进的标准」的核心机制、典型用法与常见陷阱
- 掌握「2. C++23 标准特性」的核心机制、典型用法与常见陷阱
- 掌握「3. C++20 标准特性」的核心机制、典型用法与常见陷阱
- 掌握「4. C++17 标准特性」的核心机制、典型用法与常见陷阱
- 掌握「5. C++14/11 核心特性回顾」的核心机制、典型用法与常见陷阱
拆分说明:本篇原第 6-10 节(虚函数表原理、RAII 原则、模板元编程、CMake 构建系统、vcpkg 包管理)与专篇重复,已删冗归位:虚函数表见 230 篇、RAII 见 160 篇、模板元编程见 390 篇、CMake 与 vcpkg 见 640 工具链篇。
1. C++26 与正在演进的标准
C++26 已于 2025 年 6 月完成特性冻结(feature complete),标准本体预计 2026 年底前后发布。在正式发布之前,本节所有内容均属标准草案:语法以已并入草案的提案(P 编号)为准,编译器支持参差不齐,请勿在生产代码中直接依赖。本节同时澄清几个容易与「并行算法」「C++20 constexpr」混淆的概念。
1.1 模式匹配(提案阶段,未进入标准)
结构化模式匹配(inspect,提案 P1371 / 后续 P2688)长期是社区最期待的特性之一,但截至特性冻结(2025-06)它并未并入 C++26 草案,仍处于提案阶段。以下代码仅是提案示意,不能被当前任何编译器按标准接受:
// 【非标准代码】模式匹配提案示意(P1371/P2688,未并入 C++26)
// 不要在实际项目中使用,语法与最终标准(若有)必有出入
std::string classify(int value) {
return inspect (value) {
0 => "zero";
n if n < 0 => "negative";
_ => "other";
};
}在模式匹配标准化之前,替代方案是 std::visit 配合重载 lambda(C++17 起)、或 std::holds_alternative + std::get_if 的分支组合。
1.2 契约编程(P2900,已并入草案)
C++26 草案纳入了契约编程:在函数接口上声明前置条件 pre、后置条件 post 与断言 contract_assert。注意最终并入草案的语法是语境关键字,而非早期讨论中的 [[pre: ...]] 属性写法:
// C++26 草案契约语法(P2900)
// 目前仅极少数编译器提供实验性支持,以下代码以草案文本为准
int safe_divide(int a, int b)
pre(b != 0) // 前置条件:调用方保证 b 非零
post(r : r * b == a) // 后置条件:返回值 r 满足 r*b == a
{
contract_assert(a >= 0); // 函数体内部的契约断言
return a / b;
}契约的检查语义(off / observe / enforce / quick-enforce 等检查级别)仍在标准修订中细化;早期原型的属性写法 [[assert: ...]]/[[pre: ...]] 已被上述关键字写法取代,检索旧资料时注意区分。
1.3 constexpr 的边界:哪些是 C++20 能力,哪些是 C++26 扩展
下面示例中的「constexpr 虚函数」「constexpr 标准算法」其实都是 C++20 的能力,网上资料常误标为 C++26;C++26 是在此基础上继续放宽 constexpr 限制(如 constexpr placement new、更多库函数 constexpr 化):
#include <array>
#include <algorithm>
#include <iostream>
// constexpr 虚函数与继承:这是 C++20 特性(常被误标为 C++26)
struct Shape {
constexpr virtual double area() const = 0;
constexpr virtual ~Shape() = default;
};
struct Circle : Shape {
double radius;
constexpr Circle(double r) : radius(r) {}
constexpr double area() const override { return 3.14159265 * radius * radius; }
};
struct Rectangle : Shape {
double w, h;
constexpr Rectangle(double w, double h) : w(w), h(h) {}
constexpr double area() const override { return w * h; }
};
constexpr double total_area() {
Circle c(5.0);
Rectangle r(3.0, 4.0);
return c.area() + r.area();
}
static_assert(total_area() > 0, "area should be positive");
// constexpr 标准算法排序同样是 C++20 能力
constexpr std::array<int, 5> sorted() {
std::array<int, 5> arr = {5, 3, 1, 4, 2};
std::sort(arr.begin(), arr.end());
return arr;
}
static_assert(sorted()[0] == 1);
int main() {
constexpr auto areas = total_area();
std::cout << "Total area: " << areas << "\n";
constexpr auto arr = sorted();
for (auto x : arr) std::cout << x << " ";
std::cout << "\n";
return 0;
}1.4 std::execution 与并行算法:两个容易混淆的东西
「并行执行」在 C++ 中有两个不同层次的东西,初学者经常混淆:
- C++17 并行算法:
std::execution::par等执行策略,配std::reduce/std::sort等算法使用,属<execution>头文件,与 C++26 无关; - C++26 std::execution(P2300,已并入草案):基于 sender/receiver 的异步任务图框架,解决「异步操作的组合与调度」问题,定位类似其他语言的 Future/Promise 编排。
#include <execution>
#include <algorithm>
#include <vector>
#include <numeric>
#include <iostream>
int main() {
std::vector<int> data(10'000'000);
std::iota(data.begin(), data.end(), 1);
// C++17 并行算法:执行策略(par = 并行)
auto sum = std::reduce(
std::execution::par,
data.begin(), data.end(),
0LL
);
std::cout << "Sum: " << sum << "\n";
// C++26 std::execution(P2300,草案):sender 链式组合异步任务
// auto snd = std::execution::just(42)
// | std::execution::then([](int v){ return v * 2; });
// auto [result] = std::execution::sync_wait(std::move(snd)).value();
return 0;
}2. C++23 标准特性
2.1 std::expected
std::expected<T, E> 是 C++23 引入的错误处理类型,类似于 Rust 的 Result<T, E>。
#include <expected>
#include <string>
#include <iostream>
#include <fstream>
// 使用 expected 返回值或错误
std::expected<int, std::string> parse_int(const std::string& s) {
try {
size_t pos;
int value = std::stoi(s, &pos);
if (pos != s.size()) {
return std::unexpected("trailing characters");
}
return value;
} catch (const std::exception&) {
return std::unexpected("invalid integer: " + s);
}
}
// 链式错误处理
std::expected<double, std::string> safe_divide(int a, int b) {
if (b == 0) {
return std::unexpected("division by zero");
}
return static_cast<double>(a) / b;
}
// 使用 and_then 链式操作
std::expected<double, std::string> compute(int a, int b) {
return safe_divide(a, b)
.and_then([](double v) -> std::expected<double, std::string> {
if (v < 0) return std::unexpected("negative result");
return v;
})
.transform([](double v) { return v * 2.0; });
}
int main() {
auto result = parse_int("42");
if (result) {
std::cout << "Parsed: " << *result << "\n";
} else {
std::cout << "Error: " << result.error() << "\n";
}
auto div = compute(10, 3);
std::cout << "10/3*2 = " << div.value_or(0.0) << "\n";
auto err = compute(10, 0);
if (!err) {
std::cout << "Error: " << err.error() << "\n";
}
return 0;
}2.2 显式 this 参数(推导 this)
C++23 允许将 this 作为显式参数声明,简化了常量和非常量成员函数的重复编写。
#include <iostream>
#include <string>
class Builder {
std::string data_;
int count_ = 0;
public:
// 旧写法:需要两个版本
// std::string& get_data() { return data_; }
// const std::string& get_data() const { return data_; }
// C++23 新写法:推导 this
template<typename Self>
auto&& get_data(this Self&& self) {
return std::forward<Self>(self).data_;
}
// 链式调用也简化了
template<typename Self>
auto& set_count(this Self&& self, int n) {
self.count_ = n;
return self;
}
void print() const {
std::cout << "data=" << data_ << ", count=" << count_ << "\n";
}
};
// 递归 lambda(推导 this 的另一应用)
auto fibonacci = [](this auto self, int n) -> long long {
if (n <= 1) return n;
return self(n - 1) + self(n - 2);
};
int main() {
Builder b;
b.get_data() = "hello";
b.set_count(5).print();
const Builder& cb = b;
std::cout << "const data: " << cb.get_data() << "\n";
std::cout << "fib(10) = " << fibonacci(10) << "\n";
return 0;
}2.3 std::print 与 std::println
C++23 引入了类型安全的格式化输出,替代传统的 printf 和 iostream。
#include <print>
#include <string>
#include <vector>
int main() {
std::string name = "World";
int value = 42;
double pi = 3.14159265;
// 基本格式化输出
std::print("Hello, {}!\n", name);
std::println("Value: {}", value);
std::println("Pi: {:.2f}", pi);
// 位置参数
std::println("{0} {1} {0}", "hello", "world");
// 宽度与对齐
std::println("{:>10}", "right"); // 右对齐,宽度10
std::println("{:<10}", "left"); // 左对齐,宽度10
std::println("{:^10}", "center"); // 居中,宽度10
// 数字格式
std::println("{:d}", 42); // 十进制
std::println("{:x}", 255); // 十六进制
std::println("{:#x}", 255); // 带前缀十六进制
std::println("{:b}", 10); // 二进制
std::println("{:#010b}", 10); // 带前缀补零二进制
return 0;
}2.4 std::mdspan
std::mdspan 是 C++23 引入的多维数组视图,提供对连续内存的多维访问。
#include <mdspan>
#include <vector>
#include <iostream>
int main() {
// 一维存储
std::vector<double> flat(3 * 4, 0.0);
// 创建 3x4 的二维视图
std::mdspan<double, std::extents<size_t, 3, 4>> matrix(flat.data());
// 填充数据
for (size_t i = 0; i < matrix.extent(0); ++i) {
for (size_t j = 0; j < matrix.extent(1); ++j) {
matrix[i, j] = i * 10 + j;
}
}
// 访问元素
std::cout << "matrix[1, 2] = " << matrix[1, 2] << "\n"; // 12
std::cout << "matrix[2, 3] = " << matrix[2, 3] << "\n"; // 23
// 动态维度
std::mdspan<double, std::dextents<size_t, 2>> dynamic_mat(flat.data(), 3, 4);
// 子视图(slice)
// auto row1 = std::submdspan(matrix, 1, std::full_extent);
return 0;
}3. C++20 标准特性
3.1 概念(Concepts)
概念为模板参数提供了命名约束,极大改善了模板编程的错误信息和可读性。
#include <iostream>
#include <concepts>
#include <string>
#include <vector>
// 定义概念
template<typename T>
concept Addable = requires(T a, T b) {
{ a + b } -> std::convertible_to<T>;
};
template<typename T>
concept Printable = requires(T t, std::ostream& os) {
{ os << t } -> std::convertible_to<std::ostream&>;
};
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;
// 使用概念约束模板
template<Addable T>
T add(T a, T b) {
return a + b;
}
template<Numeric T>
T multiply(T a, T b) {
return a * b;
}
// requires 子句
template<typename T>
requires requires(T a, T b) { a < b; }
T min_val(T a, T b) {
return (a < b) ? a : b;
}
// 标准库概念
template<std::ranges::range R>
void print_range(const R& r) {
for (const auto& x : r) {
std::cout << x << " ";
}
std::cout << "\n";
}
int main() {
std::cout << add(3, 4) << "\n";
std::cout << add(std::string("hello"), std::string(" world")) << "\n";
std::cout << multiply(3, 4) << "\n";
std::cout << min_val(3.14, 2.71) << "\n";
std::vector<int> v = {1, 2, 3, 4, 5};
print_range(v);
return 0;
}3.2 协程(Coroutines)
C++20 引入了协程支持,允许函数在执行中挂起和恢复。
#include <iostream>
#include <coroutine>
#include <memory>
// 简单的生成器协程
template<typename T>
struct Generator {
struct promise_type {
T current_value;
Generator get_return_object() {
return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() { throw; }
std::suspend_always yield_value(T value) {
current_value = value;
return {};
}
};
std::coroutine_handle<promise_type> handle;
Generator(std::coroutine_handle<promise_type> h) : handle(h) {}
~Generator() { if (handle) handle.destroy(); }
// 迭代器接口
bool next() {
handle.resume();
return !handle.done();
}
T value() const {
return handle.promise().current_value;
}
};
// 使用协程生成斐波那契数列
Generator<int> fibonacci() {
int a = 0, b = 1;
while (true) {
co_yield a;
auto tmp = a + b;
a = b;
b = tmp;
}
}
// 使用协程生成范围
Generator<int> range(int start, int end) {
for (int i = start; i < end; ++i) {
co_yield i;
}
}
int main() {
// 斐波那契数列前 10 个
auto fib = fibonacci();
for (int i = 0; i < 10; ++i) {
std::cout << fib.value() << " ";
fib.next();
}
std::cout << "\n";
// 范围生成
auto r = range(1, 6);
while (r.next()) {
std::cout << r.value() << " ";
}
std::cout << "\n";
return 0;
}3.3 范围(Ranges)
C++20 的 Ranges 库提供了组合式的数据处理管道。
#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>
#include <string>
int main() {
std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 管道式操作
auto result = nums
| std::views::filter([](int n) { return n % 2 == 0; }) // 过滤偶数
| std::views::transform([](int n) { return n * n; }) // 平方
| std::views::take(3); // 取前3个
for (int x : result) {
std::cout << x << " "; // 4 16 36
}
std::cout << "\n";
// 更多视图操作
auto reversed = nums | std::views::reverse | std::views::take(3);
for (int x : reversed) {
std::cout << x << " "; // 10 9 8
}
std::cout << "\n";
// 惰性求值
auto view = std::views::iota(1) // 无限序列 1,2,3,...
| std::views::filter([](int n) { return n % 3 == 0; })
| std::views::transform([](int n) { return n * 2; })
| std::views::take(5);
for (int x : view) {
std::cout << x << " "; // 6 12 18 24 30
}
std::cout << "\n";
return 0;
}3.4 模块(Modules)
C++20 模块替代了传统的头文件包含机制,提供更快的编译速度和更好的封装。
// math_module.cppm — 模块接口文件
export module math_module;
export int add(int a, int b) {
return a + b;
}
export int multiply(int a, int b) {
return a * b;
}
// 内部符号不导出
int internal_helper(int x) {
return x * x;
}// math_module_advanced.cppm — 模块分区必须写在独立的接口文件中
// 一个接口单元只能有一条 export module 声明,不能与主模块接口写在同一文件
export module math_module:advanced;
export double power(double base, int exp) {
double result = 1.0;
for (int i = 0; i < exp; ++i) result *= base;
return result;
}// main.cpp — 使用模块
import math_module;
#include <iostream>
int main() {
std::cout << add(3, 4) << "\n";
std::cout << multiply(5, 6) << "\n";
// internal_helper(5); // 错误:未导出
return 0;
}4. C++17 标准特性
4.1 结构化绑定
#include <iostream>
#include <map>
#include <tuple>
#include <array>
struct Point { double x, y, z; };
int main() {
// 绑定 pair
auto [key, value] = std::make_pair(std::string("name"), 42);
std::cout << key << " = " << value << "\n";
// 绑定 tuple
auto [a, b, c] = std::make_tuple(1, 2.0, "three");
std::cout << a << ", " << b << ", " << c << "\n";
// 绑定结构体
Point p{1.0, 2.0, 3.0};
auto [x, y, z] = p;
std::cout << "(" << x << ", " << y << ", " << z << ")\n";
// 绑定数组
std::array<int, 3> arr = {10, 20, 30};
auto [first, second, third] = arr;
// 遍历 map
std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}};
for (const auto& [name, score] : scores) {
std::cout << name << ": " << score << "\n";
}
return 0;
}4.2 if constexpr
#include <iostream>
#include <type_traits>
#include <vector>
#include <string>
template<typename T>
auto to_string(const T& value) {
if constexpr (std::is_integral_v<T>) {
return std::to_string(value);
} else if constexpr (std::is_floating_point_v<T>) {
return std::to_string(value);
} else if constexpr (std::is_same_v<T, std::string>) {
return value;
} else {
return std::string("unknown");
}
}
// 编译期分支消除
template<typename T>
void print_container(const T& container) {
if constexpr (std::is_same_v<typename T::value_type, int>) {
std::cout << "Integer container: ";
} else {
std::cout << "Other container: ";
}
for (const auto& x : container) {
std::cout << x << " ";
}
std::cout << "\n";
}
int main() {
std::cout << to_string(42) << "\n";
std::cout << to_string(3.14) << "\n";
std::cout << to_string(std::string("hello")) << "\n";
std::vector<int> vi = {1, 2, 3};
std::vector<std::string> vs = {"a", "b"};
print_container(vi);
print_container(vs);
return 0;
}4.3 std::variant / std::optional / std::filesystem
#include <iostream>
#include <variant>
#include <optional>
#include <filesystem>
#include <string>
// std::variant: 类型安全的联合体
using Value = std::variant<int, double, std::string>;
struct Visitor {
void operator()(int i) const { std::cout << "int: " << i << "\n"; }
void operator()(double d) const { std::cout << "double: " << d << "\n"; }
void operator()(const std::string& s) const { std::cout << "string: " << s << "\n"; }
};
// std::optional: 可能为空的值
std::optional<int> find_index(const std::vector<int>& v, int target) {
for (size_t i = 0; i < v.size(); ++i) {
if (v[i] == target) return static_cast<int>(i);
}
return std::nullopt;
}
int main() {
// variant 使用
Value v = 42;
std::visit(Visitor{}, v);
v = 3.14;
std::visit(Visitor{}, v);
v = std::string("hello");
std::visit(Visitor{}, v);
// optional 使用
auto idx = find_index({10, 20, 30}, 20);
if (idx) {
std::cout << "Found at index: " << *idx << "\n";
}
auto not_found = find_index({10, 20, 30}, 99);
std::cout << "Not found: " << not_found.has_value() << "\n";
// filesystem 使用
namespace fs = std::filesystem;
fs::path p = "/usr/include";
std::cout << "Path: " << p << "\n";
std::cout << "Filename: " << p.filename() << "\n";
std::cout << "Parent: " << p.parent_path() << "\n";
std::cout << "Exists: " << fs::exists(p) << "\n";
return 0;
}5. C++14/11 核心特性回顾
5.1 移动语义与完美转发
#include <iostream>
#include <utility>
#include <vector>
#include <string>
class Buffer {
int* data_;
size_t size_;
public:
// 构造函数
explicit Buffer(size_t n) : data_(new int[n]()), size_(n) {
std::cout << "Construct " << size_ << "\n";
}
// 析构函数
~Buffer() {
delete[] data_;
std::cout << "Destruct " << size_ << "\n";
}
// 拷贝构造
Buffer(const Buffer& other) : data_(new int[other.size_]), size_(other.size_) {
std::copy(other.data_, other.data_ + size_, data_);
std::cout << "Copy " << size_ << "\n";
}
// 移动构造
Buffer(Buffer&& other) noexcept : data_(other.data_), size_(other.size_) {
other.data_ = nullptr;
other.size_ = 0;
std::cout << "Move " << size_ << "\n";
}
// 拷贝赋值与移动赋值省略...
};
// 完美转发
template<typename T, typename... Args>
std::unique_ptr<T> make_unique_custom(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
int main() {
Buffer a(100); // 构造
Buffer b = a; // 拷贝构造
Buffer c = std::move(a); // 移动构造
auto ptr = make_unique_custom<Buffer>(50);
return 0;
}5.2 Lambda 表达式
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
int main() {
// 基本 lambda
auto add = [](int a, int b) { return a + b; };
std::cout << add(3, 4) << "\n";
// 捕获变量
int factor = 10;
auto multiply = [factor](int x) { return x * factor; };
std::cout << multiply(5) << "\n"; // 50
// 引用捕获
int counter = 0;
auto inc = [&counter]() { counter++; };
inc(); inc(); inc();
std::cout << "counter: " << counter << "\n"; // 3
// 初始化捕获(C++14)
auto ptr = [p = std::make_unique<int>(42)]() { return *p; };
std::cout << "ptr value: " << ptr() << "\n";
// 泛型 lambda(C++14)
auto generic = [](auto x, auto y) { return x + y; };
std::cout << generic(1, 2) << "\n";
std::cout << generic(1.5, 2.5) << "\n";
// 在算法中使用
std::vector<int> v = {5, 2, 8, 1, 9, 3};
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });
for (int x : v) std::cout << x << " ";
std::cout << "\n";
return 0;
}5.3 auto、nullptr、override/final
#include <iostream>
#include <vector>
#include <memory>
class Base {
public:
virtual void foo() { std::cout << "Base::foo\n"; }
virtual void bar() final { std::cout << "Base::bar (final)\n"; }
virtual ~Base() = default;
};
class Derived : public Base {
public:
void foo() override { std::cout << "Derived::foo\n"; }
// void bar() override {} // 错误:bar 是 final
};
int main() {
// auto 类型推导
auto x = 42; // int
auto pi = 3.14; // double
auto name = std::string("hello"); // std::string
// auto 与容器
std::vector<int> v = {1, 2, 3};
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << *it << " ";
}
std::cout << "\n";
// nullptr 替代 NULL
int* p = nullptr; // 类型安全的空指针
// int* q = NULL; // 旧写法,不推荐
// override 确保正确重写
Derived d;
Base& ref = d;
ref.foo(); // Derived::foo
return 0;
}