You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义抽象Formatter适配std/fmt后端编译失败问题排查

问题分析与解决方案

编译失败原因

  1. 标准库找不到对应formatter特化:使用std::format时,标准库会在std命名空间下查找formatter<Point>的特化,但代码仅定义了自定义的Formatter<Point>,未在std中提供对应特化,触发静态断言错误。
  2. Formatter继承与接口不匹配:直接继承Formatter<std::string>会导致上下文(context)与标准库/fmt库的要求不兼容,且两个库的formatter基类接口细节存在差异,直接继承会引发适配问题。
  3. 命名空间问题:自定义Formatter不在std命名空间中,std::format无法通过默认查找或ADL找到它。

翻译后的编译错误信息

在包含的文件中:
/opt/compiler-explorer/gcc-trunk-20240207/include/c++/14.0.1/ostream:43,
来自/opt/compiler-explorer/gcc-trunk-20240207/include/c++/14.0.1/iostream:41,
来自:1: /opt/compiler-explorer/gcc-trunk-20240207/include/c++/14.0.1/format:
在实例化'class std::__format::_Checking_scanner<char, Point>'时:
/opt/compiler-explorer/gcc-trunk-20240207/include/c++/14.0.1/format:4174:4:

__scanner(_M_str);
^~~~~~~~~ :38:29: 从此处调用 38
~~~^~ /opt/compiler-explorer/gcc-trunk-20240207/include/c++/14.0.1/format:4030:10:

(is_default_constructible_v<formatter<_Args, _CharT>> && ...),

解决方案

核心思路是通过宏切换后端,同时确保std::format和fmt::format都能找到对应类型的formatter特化,复用通用格式化逻辑。

修改后的代码如下:

#include <string>
#include <iostream>
#include <format>
#include <fmt/format.h>

struct Point {
    int x; 
    int y;
};

// 提取通用格式化逻辑,与后端无关
namespace detail {
    template <typename Context>
    auto format_point(const Point& p, Context& ctx) {
        // fmt::format_to可同时适配std和fmt的context(两者均提供out()返回输出迭代器)
        return fmt::format_to(ctx.out(), "{}us", p.x);
    }
}

#ifdef USE_FMT
// fmt后端:特化fmt::formatter
template <>
struct fmt::formatter<Point> : fmt::formatter<std::string> {
    auto format(const Point& p, fmt::format_context& ctx) const {
        return detail::format_point(p, ctx);
    }
};

#define FORMAT(fmt_str, ...) fmt::format(fmt_str, __VA_ARGS__)
#else
// std后端:在std命名空间特化formatter,满足标准库查找要求
namespace std {
    template <>
    struct formatter<Point> : formatter<std::string> {
        auto format(const Point& p, format_context& ctx) const {
            return detail::format_point(p, ctx);
        }
    };
}

#define FORMAT(fmt_str, ...) std::format(fmt_str, __VA_ARGS__)
#endif

int main() {
    Point xx {2,3};
    std::cout << FORMAT("{}", xx) << '\n';
    return 0;
}

方案说明

  1. 通用逻辑复用:将Point的格式化逻辑提取到detail命名空间,避免重复实现,同时利用fmt::format_to兼容两个后端的上下文接口。
  2. 后端适配:
    • 定义USE_FMT时,特化fmt::formatter<Point>,调用通用逻辑;
    • 未定义时,在std命名空间下特化std::formatter<Point>,满足标准库的查找规则。
  3. 统一调用入口:用FORMAT宏封装两种格式化接口,业务代码无需关心后端切换细节。

注意事项

  • 往std命名空间添加用户自定义类型的formatter特化是标准允许的行为,不会触发未定义行为。
  • fmt库的format_to函数可直接操作std::format_context的输出迭代器,无需额外拼接字符串,提升效率。

内容的提问来源于stack exchange,提问作者ATK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 08:07:47