必须特化std::formatter:需在namespace std{}内全特化template struct std::formatter,不可偏特化或放全局;parse()须校验格式并返回正确迭代器位置,不可跳过非法字符。

不特化 std::formatter,std::format 对 std::complex 或自定义矩阵类型根本无法编译——报错是 formatter not specialized for 'MyMatrix' 或更底层的 SFINAE 失败,不是写法问题,是标准强制要求。
必须在 std 命名空间内全特化 std::formatter
特化不能放在全局、不能放在类内部、不能是偏特化(比如带 template<typename t></typename>),只能是:template struct std::formatter<mymatrix char></mymatrix>,且必须包裹在 namespace std { } 里。
- 写在
.cpp文件里?链接时找不到,调用点看不到特化,std::format退化为尝试 ADL 查找(失败)或报错 - 写成
template<typename t> struct std::formatter<matrix>, char></matrix></typename>?标准禁止,Clang/GCC 都会静默忽略,MSVC 可能直接报错 - 头文件中定义但没
#include到使用点?和没写一样;建议把特化紧接在类定义之后,同在一个头文件 - 要支持宽字符?还得额外加一份
std::formatter<mymatrix wchar_t></mymatrix>,char不是万能默认
parse() 必须返回合法迭代器位置,不能“跳过非法字符”
parse() 的职责不是“尽力解析”,而是“守界校验”。比如你只支持 :f(浮点格式)和 :e(科学计数),用户写了 {:f4},你得在 '4' 处停住并返回指向它的迭代器;若直接返回 ctx.end(),std::format 会认为整个说明符合法,后续 format() 可能读到未初始化的宽度值,输出错乱甚至崩溃。
- 返回类型必须是
decltype(ctx.begin()),别用auto——某些模板上下文里推导成const char*导致编译失败 - 检查数字要用
std::isdigit(*it),别手写*it >= '0' && *it ,宽字符下失效 - 遇到不支持的字符(如
'x'对复数无意义),应抛std::format_error("unknown specifier 'x'"),不能静默跳过 - 常见错误:只写
return ctx.end();就完事——这等于放弃所有格式控制能力,但至少能编译通过;若想支持:10宽度,就得自己提取并存为成员变量(如width_)
format() 里禁用 std::format,改用 std::format_to
在 format() 函数里调用 std::format("{}", elem) 是最隐蔽的陷阱:它会触发对 elem 类型的 std::formatter 重载查找,如果 elem 是另一个也实现了 std::formatter 的类型(比如 std::complex<float></float>),就会引发无限模板实例化,编译卡死或爆内存。
立即学习“C++免费学习笔记(深入)”;
- 正确做法:统一走
std::format_to(ctx.out(), "{:.3f}", elem),它把结果直接写入已有输出迭代器,不新建上下文 - 对
std::complex<float></float>字段,不用手动拼real() ——只要它自己有 <code>std::formatter特化,format_to就能自动调用 - 输出必须走
ctx.out(),不能用std::ostringstream或+=拼字符串——否则丢失对齐、填充、精度等标准控制能力 - 私有成员访问不到?加
friend struct std::formatter<mymatrix char>;</mymatrix>最干净;暴露公有data()或at(row, col)接口也可行
复数与矩阵的典型特化结构差异
复数类型(如 std::complex<float></float>)通常只需控制实部/虚部精度和符号分隔符;矩阵则需处理行列遍历、元素对齐、嵌套括号。二者 parse() 解析逻辑不同,但 format() 都依赖 format_to 递归写入。
- 复数示例中
parse()可能只识别:f/:e和可选精度(:.3f),而矩阵可能还要支持:row(单行输出)、:compact(省略空格)等语义 - 矩阵
format()里嵌套循环调用format_to写每个元素时,记得传入当前字段(如m(i,j)),别传整个矩阵对象——否则又触发递归 - 若矩阵元素类型本身不支持
std::formatter(比如裸float),format_to(ctx.out(), "{:.2f}", m(i,j))仍有效,因为std::formatter<float></float>是标准内置的 - 宽字符支持不是“加个
wchar_t模板参数”就完事:所有字符串字面量(如"(")得换成L"(",std::format_to的格式串也要匹配字符类型
最容易被忽略的是:parse() 返回位置决定 std::format 是否认为说明符“已完全消费”,哪怕你什么也不支持,也得返回一个明确位置;还有就是所有特化必须在调用前可见——不是声明了就行,是定义必须被包含进来。


















