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

如何为模板类定义通用fmt格式化器?以cv::Point_为例

为OpenCV模板类定义fmt格式化器的问题与解决方案

问题描述

尝试为OpenCV的cv::Point_这类模板类定义可复用的fmt格式化器,单独编译代码无问题,但使用时出现编译错误。初始代码如下:

template<typename T>
struct fmt::formatter<cv::Point_<T>> : fmt::formatter<std::string_view> {
    template<typename FormatContext>
    auto format(const cv::Point_<T>& point, FormatContext& ctx) const {
        // return format_to(ctx.out(), "Point(x={}, y={})", point.x, point.y);
        return fmt::format("Point(x={}, y={})", point.x, point.y, ctx);
    }
};

编译器提示无法证明类型T具备可格式化能力,尝试用算术概念约束T也无效。

编译错误信息

错误信息1

/usr/local/include/fmt/ostream.h: At global scope:
/usr/local/include/fmt/ostream.h:185:8: error: partial specialization of ‘struct fmt::v9::detail::fallback_formatter<T, Char, typename std::enable_if<fmt::v9::detail::is_streamable<T, Char>::value, void>::type>’ after instantiation of ‘struct fmt::v9::detail::fallback_formatter<cv::Point_<float>, char, void>’ [-fpermissive]
  185 | struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
      |        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

错误信息2

/usr/local/include/fmt/core.h:2743:12: error: use of deleted function ‘fmt::v9::detail::fallback_formatter<T, Char, Enable>::fallback_formatter() [with T = cv::Rect_<float>; Char = char; Enable = void]’
 2743 |   auto f = conditional_t<has_formatter<mapped_type, context>::value,
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 2744 |                          formatter<mapped_type, char_type>,
      |                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 2745 |                          fallback_formatter<stripped_type, char_type>>();
      |                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/local/include/fmt/core.h:1124:3: note: declared here
 1124 |   fallback_formatter() = delete;
      |   ^~~~~~~~~~~~~~~~~~
/usr/local/include/fmt/core.h: In instantiation of ‘constexpr fmt::v9::detail::value<Context> fmt::v9::detail::make_value(T&&) [with Context = fmt::v9::basic_format_context<fmt::v9::appender, char>; T = const cv::Rect_<float>&]’:
/usr/local/include/fmt/core.h:1777:29:   required from ‘constexpr fmt::v9::detail::value<Context> fmt::v9::detail::make_arg(T&&) [with bool IS_PACKED = true; Context = fmt::v9::basic_format_context<fmt::v9::appender, char>; type <anonymous> = fmt::v9::detail::type::custom_type; T = const cv::Rect_<float>&; typename std::enable_if<IS_PACKED, int>::type <anonymous> = 0]’
/usr/local/include/fmt/core.h:1901:77:   required from ‘constexpr fmt::v9::format_arg_store<Context, Args>::format_arg_store(T&& ...) [with T = {const cv::Rect_<float>&, const float&, const int&}; Context = fmt::v9::basic_format_context<fmt::v9::appender, char>; Args = {cv::Rect_<float>, float, int}]’
/usr/local/include/fmt/core.h:1918:31:   required from ‘constexpr fmt::v9::format_arg_store<Context, typename std::remove_cv<typename std::remove_reference<_Args>::type>::type ...> fmt::v9::make_format_args(Args&& ...) [with Context = basic_format_context<appender, char>; Args = {const cv::Rect_<float>&, const float&, const int&}]’
/usr/local/include/fmt/core.h:3206:44:   required from ‘std::string fmt::v9::format(format_string<T ...>, T&& ...) [with T = {const cv::Rect_<float>&, const float&, const int&}; std::string = std::__cxx11::basic_string<char>; format_string<T ...> = basic_format_string<char, const cv::Rect_<float>&, const float&, const int&>]’


/usr/local/include/fmt/core.h:1757:7: error: static assertion failed: Cannot format an argument. To make type T formattable provide a formatter<T> specialization: https://fmt.dev/latest/api.html#udt
 1757 |       formattable,
      |       ^~~~~~~~~~~
/usr/local/include/fmt/core.h:1757:7: note: ‘formattable’ evaluates to false

解决方案

你的代码存在两个核心问题:

  1. 错误调用格式化接口:在格式化器的format方法里,不能直接调用fmt::format并传入ctx,正确做法是用fmt::format_to直接写入上下文的输出迭代器,这是自定义格式化器的标准用法。
  2. 缺少类型约束:需要明确约束T是fmt可格式化的类型,避免编译器触发无效的fallback formatter逻辑。

正确的格式化器定义

#include <fmt/format.h>
#include <opencv2/core/types.hpp>

// 为cv::Point_<T>定义格式化器,约束T必须是可格式化类型
template<typename T>
requires fmt::formattable<T>
struct fmt::formatter<cv::Point_<T>> : fmt::formatter<T> {
    template<typename FormatContext>
    auto format(const cv::Point_<T>& point, FormatContext& ctx) const {
        // 直接写入上下文输出流,复用T的格式化规则
        return fmt::format_to(ctx.out(), "Point(x={}, y={})", point.x, point.y);
    }
};

关键说明

  • 约束T的可格式化性:通过requires fmt::formattable<T>确保只有当T本身能被fmt格式化时,该特化才生效,避免编译器尝试错误的回退逻辑。
  • 继承fmt::formatter:相比继承std::string_view,这样能直接复用T的格式化规则(比如浮点数精度控制),灵活性更高。
  • 使用format_to而非format:format_to直接操作上下文的输出迭代器,符合fmt自定义格式化器的设计规范,不会生成额外临时字符串,效率更高。

扩展到cv::Rect_等其他模板类

同样逻辑可复用在cv::Rect_<T>上:

template<typename T>
requires fmt::formattable<T>
struct fmt::formatter<cv::Rect_<T>> : fmt::formatter<T> {
    template<typename FormatContext>
    auto format(const cv::Rect_<T>& rect, FormatContext& ctx) const {
        return fmt::format_to(ctx.out(), "Rect(x={}, y={}, width={}, height={})",
                              rect.x, rect.y, rect.width, rect.height);
    }
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 16:34:54