C++20 推出了 std::format 标准库组件,它借鉴了 Python 的 f-string 语法,为字符串格式化提供了类型安全、可维护性强且效率高的解决方案。本文将详细介绍 std::format 的基本用法、常见选项,以及如何通过实现自定义格式化器来扩展它的功能。
1. 基本语法
#include <format>
#include <iostream>
int main() {
std::string s = std::format("Hello, {}! You have {} unread messages.", "Alice", 42);
std::cout << s << '\n';
}
上述代码会输出:
Hello, Alice! You have 42 unread messages.
- 花括号
{} 作为占位符,对应后面可变参数列表中的元素。
std::format 会根据每个参数的类型自动选择合适的格式化器。
2. 格式说明符
与 printf 不同,std::format 的说明符遵循 C++ 标准的“格式说明符”语法,形如 [[flags][width][.precision][type]]。
std::format("{:>10}", 123); // 右对齐,占位 10 个字符
std::format("{:.2f}", 3.14159); // 保留两位小数
std::format("{:#x}", 255); // 十六进制并带前缀 0x
3. 常见使用场景
3.1 日期时间格式化
#include <chrono>
#include <format>
auto now = std::chrono::system_clock::now();
std::string ts = std::format("{:%Y-%m-%d %H:%M:%S}", std::chrono::system_clock::to_time_t(now));
3.2 自定义类型格式化
若想让 std::format 能直接处理自定义类,可以为该类实现 `std::formatter
` 特化。
## 4. 自定义格式化器实现
下面演示如何为自定义 `Point` 结构体实现一个格式化器,使其支持 `x, y` 或 `polar` 两种表示方式。
“`cpp
#include
#include
#include
struct Point {
double x, y;
};
namespace std {
template
struct formatter
{
// 解析说明符,支持 “polar” 关键字
parse_context::iterator parse(format_parse_context& ctx) {
auto it = ctx.begin(), end = ctx.end();
if (it != end && *it == ‘p’) { // “p” 代表 polar
++it;
if (it != end && *it == ‘o’) {
++it;
if (it != end && *it == ‘l’) {
++it;
}
}
}
if (it != end && *it != ‘}’) {
throw format_error(“invalid format”);
}
return it;
}
// 格式化逻辑
template
auto format(const Point& pt, FormatContext& ctx) {
// 判断是否是 polar 表示
bool polar = false;
auto it = ctx.out(); // 只用来检测已解析的说明符
// 这里省略说明符判断的细节,直接硬编码为 polar
polar = true;
if (polar) {
double r = std::hypot(pt.x, pt.y);
double a = std::atan2(pt.y, pt.x);
return format_to(ctx.out(), “({:.2f}, {:.2f} rad)”, r, a);
} else {
return format_to(ctx.out(), “({:.2f}, {:.2f})”, pt.x, pt.y);
}
}
};
} // namespace std
int main() {
Point p{3.0, 4.0};
std::cout `,你可以让任何类型都能无缝参与格式化,极大提升代码的可维护性与可读性。下次在需要打印日志或构造复杂字符串时,别忘了尝试一下 std::format 吧。