自 C++11 起,C++ 经历了一场脱胎换骨的现代化变革。移动语义、Lambda、智能指针让 C++ 从"带类的 C"演变为一门表达能力媲美高层语言的系统级语言;C++20 的 Concepts、Ranges、Coroutines 三大特性更是将 C++ 推向了新的抽象高度;C++23 和即将到来的 C++26 则继续在并发、容器、类型系统等方向深耕。
本专题旨在逐一解剖这些关键特性——不讲"是什么"的表层,而是追问"为什么这样设计""编译器在背后做了什么""正确用法和致命陷阱分别在哪里"。
自 C++11 起,C++ 经历了一场脱胎换骨的现代化变革。移动语义、Lambda、智能指针让 C++ 从"带类的 C"演变为一门表达能力媲美高层语言的系统级语言;C++20 的 Concepts、Ranges、Coroutines 三大特性更是将 C++ 推向了新的抽象高度;C++23 和即将到来的 C++26 则继续在并发、容器、类型系统等方向深耕。
本专题旨在逐一解剖这些关键特性——不讲"是什么"的表层,而是追问"为什么这样设计""编译器在背后做了什么""正确用法和致命陷阱分别在哪里"。
在 C++98 时代,编译期计算几乎等同于模板元编程(TMP)——用递归模板 + 特化在类型系统里模拟图灵完备的计算。代码晦涩,编译慢,出错信息堪称灾难。C++11 引入了 constexpr,承诺让"正常 C++ 代码"在编译期运行。此后每一个标准版本都在放宽它的能力边界,到 C++20/23,你几乎可以在编译期做任何事。
本文梳理 constexpr 从 C++11 到 C++23 的演进历程,重点解释 if constexpr、consteval、constinit 三个极易混淆的概念,以及编译期容器的内存模型。
C++ 的零拷贝(zero-copy)设计哲学有一条清晰的演进路线:从 C 语言的裸指针,到 C++98 的引用,再到 C++17 的 string_view 和 C++20 的 span<T>。这条路的终点是:函数参数只描述"我读什么",而不参与所有权决策,也不触发拷贝和分配。
与零拷贝参数字段平行发展的,是标准库对操作系统设施的封装——C++17 的 <filesystem> 第一次让目录遍历、路径操作、文件属性查询成为了标准 C++,不再依赖 POSIX 或 Win32 API。
如果说 C++11 是一次"革命"(移动语义、Lambda、智能指针),C++20 是一次"进化"(Concepts、Ranges、Coroutines),那么 C++17 的核心贡献是语法上的精耕细作——没有改变语言的根本表达范式,但在大量日常编码路径上,把繁琐的手写代码变成了编译器自动推导。
本文详述 C++17 三大语法糖:结构化绑定、折叠表达式和类模板实参推导(CTAD),它们各自只涉及很少的语法规则,但覆盖了极其广阔的日常使用场景。
C++17 没有 C++20 那种"换代级"的大语法(Concepts/Ranges/Coroutines),但它引入了一组极大改善日常表达力的类型——variant、optional 和 any。三个类型分别对应三种不同的运行时不确定性:
optional<T>:要么有 T,要么没有("可空的值")variant<Ts...>:是 Ts 中的恰好一种("多个可能类型中的一个")any:可以是任意可拷贝类型("完全放弃类型信息")这三者一起,构成了从"最确定"(optional)到"最灵活"(any)的运行时多态谱系。其中 variant 最为精妙——它不仅是 C++ 对 sum type 的首次标准实现,还带着一套 std::visit 的模式匹配机制。