std::format要求自定义类型必须特化std::formatter;需定义parse()、format()和char_type;特化须在std命名空间内全特化,且确保头文件中可见。
std::formatter
直接对自定义类调用 std::format 会编译失败,报错类似:error: no matching function for call to 'format' 或更具体的 formatter not specialized for 'MyClass'。
根本原因是:C++20 的 std::format 不支持自动反射,必须显式提供格式化逻辑——即特化模板 std::formatter。
std::formatter 的最小必要成员一个能通过编译并被 std::format 识别的特化,至少需定义三样东西:
parse():解析格式说明符(如 "{:x}" 中的 x),返回迭代器位置;若不支持任何说明符,可只写 return ctx.end();
format():执行实际格式化,接收 const MyClass& 和 format_context&,返回 format_context::iterator
constexpr 静态成员 char_type(通常为 char)注意:format() 内部应使用 ctx.out() 输出字符,不能直接写 std::cout;parse() 若忽略说明符,也必须实现,不可留空或删去。
Point 类添加 std::formatter
struct Point {
int x, y;
};
template<>
struct std::formatter {
constexpr auto parse(std::format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.end(); // 不处理任何格式说明符
}
template
auto format(const Point& p, FormatContext& ctx) const {
return std::format_to(ctx.out(), "({},{})", p.x, p.y);
}
};
// 使用
int main() {
Point p{3, 4};
std::string s = std::format("Origin: {}", p); // → "Origi
n: (3,4)"
}
几个容易卡住的点:
std 命名空间内,且必须是全特化(不能是偏特化),否则链接或匹配失败std::format;GCC 12 默认仍用 libstdc++ 的实验性实现,可能缺失部分特性format() 无法直接访问——要么加 friend,要么提供公有 to_string() 辅助函数parse() 返回值类型必须严格匹配(decltype(ctx.begin())),手写 auto 容易因上下文推导出错最常被忽略的是:忘记把特化放在头文件中、或放在类定义之后但未确保所有使用点都能看到该特化——它不像 ADL 查找,必须可见才能参与重载决议。