现代 C++ 实战(09):C++20 格式化与编译期计算
printf 类型不安全,iostream 又冗又慢;C++20 的 std::format 把 {fmt} 收进标准库,Python 风格占位符 + 编译期类型检查。同一时代还有 constexpr 进阶、consteval、std::span——把格式化与计算都推向「更安全、更编译期」。
这一篇对应三个 demo:format_demo、constexpr_demo、span_demo。
这是「现代 C++ 实战」系列的第 9 篇。建议先读 第 08 篇:错误处理策略。
一、格式化三时代
| 方式 | 类型安全 | 语法 | 性能 |
|---|---|---|---|
printf |
❌ 格式与参数不匹配 → UB | C 风格 %d |
快 |
iostream |
✅ | cout << x << y 冗长 |
较慢 |
std::format |
✅ | "{} + {} = {}" 简洁 |
接近 printf |
C++23 再加 std::print / std::println——直接输出,不必先拼 string 再 cout。
编译器要求:GCC 13+ / Clang 17+ 支持 std::format;std::print 需 GCC 14+ / Clang 18+。
二、std::format 基础
1 |
|
| 占位符 | 含义 |
|---|---|
{} |
自动格式化 |
{0}, {1} |
按位置索引 |
{:d} / {:f} / {:s} |
整数 / 浮点 / 字符串 |
{:>10} / {:<10} / {:^10} |
右 / 左 / 居中 |
{:.2f} |
精度 |
自定义类型:实现 formatter<T> 特化,即可用 {} 格式化你的 struct——demo 里有完整示例。
三、std::print(C++23)
1 |
|
等价于 std::format + 写入 stdout,代码更短。不支持时可用 {fmt} 库或继续 format + cout。
四、constexpr 演进
核心思想:参数为编译期常量时,函数在编译期求值——零运行时开销。
| 标准 | constexpr 能力 |
|---|---|
| C++11 | 函数体只能有一条 return |
| C++14 | 允许循环、局部变量、分支 |
| C++17 | if constexpr、lambda 可 constexpr |
| C++20 | 更多标准库算法/容器可 constexpr |
1 | constexpr int factorial(int n) { |
用途:数组大小、模板非类型参数、static_assert 校验、查表预计算。
五、consteval 与 constinit(C++20)
consteval:必须在编译期求值
1 | consteval int sq(int n) { return n * n; } |
与 constexpr 的区别:constexpr「可以」编译期求值;consteval「必须」编译期求值。
constinit:静态变量编译期初始化
1 | constinit int global_counter = compute_at_compile_time(); |
解决静态初始化顺序问题——保证在动态初始化之前已有确定值(仍可能运行期赋值,但首次初始化在编译期完成)。
if consteval(C++23)
1 | constexpr int f(int n) { |
同一函数内区分编译期 / 运行期实现。
六、std::span:连续内存的非拥有视图
类似 string_view 之于 string,span 不拥有内存,只持有指针 + 长度:
1 |
|
| 类型 | 大小 |
|---|---|
span<T> |
动态长度,指针 + size |
span<T, N> |
固定长度 N,仅指针 |
替代裸指针 + length 参数,边界更清晰;与 Ranges 配合良好(第 10 篇)。
注意:span 生命周期不能长于底层容器——不延长对象寿命。
七、三个 demo 怎么跑
1 | cd ref/cpp_demo/basics/format_demo && ./build.sh --run |
CMake 建议 -std=c++20;需要 C++23 特性时设 -std=c++23。
八、小结
| 特性 | 要点 |
|---|---|
std::format |
类型安全、 {} 占位符,替代 printf/iostream |
std::print |
C++23 一行输出 |
constexpr |
编译期计算,C++14 起可写循环 |
consteval |
强制编译期 |
constinit |
静态变量编译期首初始化 |
span |
非拥有连续内存视图 |
现代 C++ 实战系列第 9 篇完。下一篇 Ranges 与函数式风格——
filter | transform | take管道。
系列导航
| 篇号 | 标题 | 状态 |
|---|---|---|
| 08 | 错误处理策略 | ✅ |
| 09 | C++20 格式化与编译期计算(本篇) | ✅ |
| 10 | Ranges 与函数式风格 | 下一篇 |
完整大纲见工作区 docs/CPP_SERIES_OUTLINE.md。









