C++20 引入了 std::format 标准库,它提供了类似 Python 的格式化语法,并且保证在编译时检查格式字符串和参数的一致性。相比于老式的 printf 或者字符串拼接,std::format 能够在运行时防止缓冲区溢出、格式错误和类型不匹配等常见问题。下面我们逐步介绍它的使用方法、常见技巧以及注意事项。
1. 依赖与准备
- 编译器:支持 C++20 的 GCC 10+、Clang 12+、MSVC 19.28+。
- 需要包含头文件 `#include `,并开启 C++20 标准(如 `-std=c++20`)。
#include <iostream>
#include <format>
#include <string>
提示:若使用的是 GCC 或 Clang 版本较低,可能需要安装
libfmt并链接-lfmt。
2. 基本用法
int main() {
std::string name = "Alice";
int age = 30;
std::string msg = std::format("My name is {}, age {}.", name, age);
std::cout << msg << '\n';
}
输出:
My name is Alice, age 30.
位置参数
可以使用 {0}, {1} 指定位置,支持重用:
auto s = std::format("{0} scored {1} points. {0} is happy.", "Bob", 95);
关键字参数
C++20 std::format 也支持关键字参数,类似 Python 的命名参数:
auto s = std::format("{name} has {score} points", std::format_args{fmt::arg("name", "Carol"), fmt::arg("score", 88)});
注意:
fmt::arg需要包含#include <fmt/args.h>,在标准库中已简化。
3. 格式说明符
格式说明符位于 {} 内部,语法:
{[argument_index][!conversion][:format_spec]}
!conversion:转义方式,常见有!s、!r、!a(C++20 未实现!r、!a,可使用!s进行字符串转义)。:format_spec:详细格式,例如对齐、宽度、填充、精度、进制等。
对齐与宽度
std::cout << std::format("{:10}", 42); // 右对齐,宽度10
std::cout << std::format("{:<10}", 42); // 左对齐
std::cout << std::format("{:^10}", 42); // 居中
填充字符
std::cout << std::format("{:0>5}", 42); // 00042
数值进制
std::cout << std::format("{:b}", 10); // 1010
std::cout << std::format("{:o}", 10); // 12
std::cout << std::format("{:X}", 255); // FF
浮点数精度
std::cout << std::format("{:.2f}", 3.14159); // 3.14
4. 安全性与错误检测
编译时检查
std::format 在编译时会检查占位符数量与参数数量的一致性。例如:
std::format("{:d} {} {}", 10, "hello");
将导致编译错误,提示缺失参数。
运行时异常
如果格式字符串与参数类型不匹配,std::format 会抛出 std::format_error。
try {
std::format("{:d}", "not an int");
} catch (const std::format_error& e) {
std::cerr << "格式错误: " << e.what() << '\n';
}
5. 性能对比
与 sprintf/printf 或手动拼接相比,std::format 的性能略高,主要得益于:
- 避免了不安全的缓冲区操作。
- 编译期类型检查减少了运行时错误。
- 在大多数实现中使用了
fmt库的高效算法。
6. 高级用法
自定义格式化器
可以为自定义类型实现 `std::formatter
` 以支持 `std::format`。 “`cpp struct Point { int x, y; }; template struct std::formatter { constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } template auto format(const Point& p, FormatContext& ctx) { return format_to(ctx.out(), “Point({}, {})”, p.x, p.y); } }; int main() { Point p{3, 4}; std::cout (std::pmr::new_delete_resource()); int* arr = p.allocate(10, std::align_val_t{16}); “` ## 7. 常见陷阱 | 场景 | 错误 | 解决方案 | |——|——|———-| | 位置与关键字混用 | `std::format(“{0} {name}”, 1, fmt::arg(“name”,”A”))` | 只能使用一种方式,或者显式指定全部关键字参数。 | | 格式说明符错误 | `”{:d}”` 用于字符串 | 该格式符要求整数,使用 `{:s}` 或不使用说明符。 | | 格式化大型字符串 | 频繁拼接导致性能低 | 直接使用 `std::format` 或 `std::format_to` 写入缓冲区。 | ## 8. 小结 – `std::format` 是 C++20 标准提供的安全、强大、易用的字符串格式化工具。 – 它通过编译期检查、类型安全以及丰富的格式化选项,减少了许多传统 C 风格字符串操作的坑。 – 通过自定义 `formatter` 可以让任何类型都能轻松参与格式化,极大提升代码可读性与维护性。 建议在新项目中直接使用 `std::format` 替代老旧的字符串拼接与 `printf`,既能提升安全性,也能让代码更加现代化。