发布于2026-07-07 阅读(0)
扫一扫,手机访问
写 C++20 的 std::format 时,如果你想格式化自己的类,第一反应多半是:能不能直接 std::format("{}", my_obj)?
结论很干脆——不特化 std::formatter,一定会编译失败。编译器会甩给你一堆诸如 no matching function for call to 'format' 或 formatter not specialized for 'MyClass' 的报错。这不是配置有误,而是 C++20 标准定下的硬性要求:没有反射机制,你就得亲手写出格式化逻辑。
编译器只认 std::formatter 在 std 命名空间下的全特化版本。偏特化,比如 template,标准层面是不认的。大多数编译器要么静默无视,要么直接报错。
这里有几点需要注意:
namespace std { ... } 里,用 using 或别名去绕是行不通的Point 和 char。如果你想支持宽字符,换成 wchar_t 就行point.h,那 formatter 特化要么放在同一头文件末尾,要么确保每个包含 point.h 的源文件都能看到它std::format 都没启用,需要加编译选项 -std=c++20 -D_GLIBCXX_USE_CXX11_ABI=1parse() 的作用是解析你传进来的格式说明符。比如 "{:x}" 中的 x,这个东西得由它来认领。就算你的类完全不支持任何修饰符,也得有个函数体,否则链接期或运行期行为就没法保证了。
最安全也最简单的写法是 return ctx.end();。这表示“没找到有效说明符,全当不存在”。但千万别写成 return ctx.begin();——那会让格式化器以为说明符还没读完,后续 format() 很可能读到非法状态。
如果你想支持自定义说明符,比如 "{:short}",那就得手动遍历从 ctx.begin() 到 ctx.end() 之间的内容,遇到 '}' 就停止,碰到非法输入就抛 std::format_error。
另外,返回类型必须是 decltype(ctx.begin())。用 auto 推导通常没问题,但遇上嵌套模板这类复杂上下文时,显式写成 std::format_parse_context::iterator 会更稳妥。
format() 的职责是把字符“写入输出迭代器”,而不是打印到屏幕或拼接字符串。它接收的是 FormatContext&,其中的 ctx.out() 返回一个输出迭代器(类似 std::back_insert_iterator),所有输出都要通过它来完成。
正确写法是:return std::format_to(ctx.out(), "({},{})", p.x, p.y);
错误写法则是:std::cout << ...——输出目标错了,返回值类型也对不上。
如果类的成员是私有的,比如 int x_, y_;,那 format() 内部就无法直接访问。要么在类定义中加上 friend struct std::formatter,要么提供公有的 int x() const 这样的访问器。
容易被忽略的一点是:特化必须在每个使用 std::format(..., my_obj) 的翻译单元里都可见。 把它塞在 .cpp 里?不行。放在类定义之前?也不行——因为 std::formatter 模板参数依赖 Point 的完整定义。
唯一稳妥的做法是:把类定义、formatter 特化、还有 #include 全部收在同一个头文件里,并确保用户只 #include 这一个头文件。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8