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

如何为自定义to_string模板实现通用fmt::formatter?

复用自定义to_string函数实现fmt通用格式化

需求

已有一组自定义的to_string模板特化,希望让fmt::format自动调用这些函数格式化对应类型,无需为每个类型单独编写fmt::formatter特化。

现有自定义to_string代码:

struct A {};
struct B {};

template <typename T>
std::string to_string(const T&);

template <>
std::string to_string(const A&) { return "A"; }

template <>
std::string to_string(const B&) { return "B"; }

目标是直接通过fmt::format格式化类型实例:

int main() {
    A a;
    B b;
    std::cout << fmt::format("{} {}\n", a, b); // 期望输出 "A B"
}

当前冗余实现

目前可以为每个类型单独编写fmt::formatter特化,但这种方式重复代码多,扩展性差:

template <>
struct fmt::formatter<A> {
    fmt::formatter<fmt::string_view> str_formatter;

    constexpr auto parse(format_parse_context& ctx) {
        return str_formatter.parse(ctx);
    }

    template <typename FormatContext>
    auto format(const A& a, FormatContext& ctx) const {
        return str_formatter.format(::to_string<A>(a), ctx);
    }
};

template <>
struct fmt::formatter<B> {
    fmt::formatter<fmt::string_view> str_formatter;

    constexpr auto parse(format_parse_context& ctx) {
        return str_formatter.parse(ctx);
    }

    template <typename FormatContext>
    auto format(const B& b, FormatContext& ctx) const {
        return str_formatter.format(::to_string<B>(b), ctx);
    }
};

通用化尝试失败

直接全模板特化fmt::formatter<T>会覆盖fmt库原本对所有类型的格式化支持(如int、std::string等),导致编译错误,因此该方案不可行:

// 错误方案:会覆盖fmt原生的formatter特化
template <typename T>
struct fmt::formatter<T> {
    fmt::formatter<fmt::string_view> str_formatter;

    constexpr auto parse(format_parse_context& ctx) {
        return str_formatter.parse(ctx);
    }

    template <typename FormatContext>
    auto format(const T& obj, FormatContext& ctx) const {
        return str_formatter.format(::to_string<T>(obj), ctx);
    }
};

通用化解决方案

利用C++17的SFINAE特性,通过类型检测仅为存在自定义to_string的类型生成fmt::formatter特化,同时复用fmt::formatter<string_view>的解析逻辑:

#include <fmt/core.h>
#include <type_traits>

// 辅助 trait:检测类型T是否存在有效的::to_string(const T&)重载
template <typename T, typename = void>
struct has_custom_to_string : std::false_type {};

template <typename T>
struct has_custom_to_string<T, std::void_t<decltype(::to_string(std::declval<const T&>()))>> : std::true_type {};

// 偏特化fmt::formatter,仅对有自定义to_string的类型生效
template <typename T>
struct fmt::formatter<T, std::enable_if_t<has_custom_to_string<T>::value>> : fmt::formatter<fmt::string_view> {
    template <typename FormatContext>
    auto format(const T& obj, FormatContext& ctx) const {
        // 调用自定义to_string后,复用string_view的格式化逻辑
        return fmt::formatter<fmt::string_view>::format(::to_string(obj), ctx);
    }
};

说明

  1. has_custom_to_string trait通过std::void_t和表达式SFINAE检测::to_string(const T&)是否可调用;
  2. 偏特化的fmt::formatter继承自fmt::formatter<string_view>,自动复用其parse方法,无需重复编写;
  3. 仅对满足has_custom_to_string<T>::value为true的类型生效,不会干扰fmt原生支持的类型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 02:21:14