需在 std::formatter::parse 中解析格式字符串,提取对齐(、^)、宽度(整数)和填充字符(默认空格),存入 formatter 成员;再于 format 中传给 std::format_to,否则对齐无效。

std::format 本身不支持为自定义类型自动推导对齐宽度或填充字符——这些必须在 std::formatter 特化中手动解析并传给内部 std::format_to 调用,否则对齐和补齐逻辑不会生效。
如何在自定义 formatter 中解析对齐/宽度/填充符
std::format 的格式说明符(如 {:)在进入你的 <code>parse() 函数时,是以字符迭代器形式传入的。你不能跳过这部分解析,否则 format() 里就拿不到宽度或填充符。
- 填充符(如
*)必须紧邻对齐符号(、<code>^、>),且只能是单字节 ASCII 字符;parse()需显式读取它并存为成员变量 - 宽度(如
10)是十进制整数字面量,需用std::from_chars或手动解析(注意:不能用std::stoi,因parse()要求constexpr友好) - 对齐符号本身要识别并记录,比如
align = ',后续在 <code>format()中决定调用std::format_to(ctx.out(), "{: 还是其他变体 - 若格式串含精度(如
{:.3f}),它只对数值类型有意义;对自定义类型,你得自己定义精度语义(比如控制子字段小数位数)
为什么不能在 format() 里直接用 std::format("{}", value)
这会触发无限递归:因为 std::format("{}", value) 内部仍会查找 std::formatter<decltype></decltype>,而当前正在执行的就是它的 format() 成员函数。
- 正确做法是调用
std::format_to(ctx.out(), "...", ...),把输出写入已有迭代器 - 对子字段(如成员变量
x、y),继续用std::format_to,它会自动路由到对应类型的std::formatter特化(包括你刚写的或标准库内置的) - 如果子字段是基本类型(
int、double、std::string_view),可安全使用std::format_to(ctx.out(), "{: —— 但注意:C++20 不支持运行时宽度作为模板参数,所以这里 <code>width必须是编译期常量;若需动态宽度,得降级用std::vformat
C++20 中动态对齐宽度的唯一可行路径是 std::vformat
你无法在 std::format 字面量格式串中写 "{: 并传入运行时变量 <code>width,C++20 编译器会直接报错:width argument to format specifier is not a constant expression。
- 替代方案:拼接格式串,再用
std::vformat+std::make_format_args - 例如:
std::vformat("{: - 性能敏感场景(如万行表格),建议提前生成各列格式串缓存(
std::vector<:string></:string>),避免每次重复std::to_string和字符串拼接 - 注意:
std::vformat比std::format开销略大(需解析运行时格式串),但它是在 C++20 下实现动态对齐的**唯一标准方式**
最易被忽略的一点:自定义 std::formatter 的 parse() 函数返回的是格式串剩余部分的迭代器,若解析不完整(比如漏掉填充符或宽度后多出字符),会导致后续 format() 被跳过或行为未定义——务必让 parse() 消费完所有你关心的字符,并返回准确位置。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











