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

C++20下无自定义formatter时std::format输出key的实现方案

C++20中实现可降级的格式化断言输出方案

需求背景

我在自定义断言宏中使用std::format,希望编写如下代码:

assert( is_found(key) ) << std::format( "The key '{}' was not found", key );

问题在于key的类型由用户提供,我不想强制调用者为该类型实现自定义formatter,但对已有formatter的类型要保留正常格式化功能;当没有可用的formatter时,希望输出:

The key '(no user supplied formatter)' was not found.

有没有现成的实现方案?我也可以接受编写如下代码:

assert( is_found(key) ) << std::format( "The key '{}' was not found", TryToFormat(key) );

其中TryToFormat会在存在formatter时返回原类型的key,不存在时返回MissingT<T>,我会为MissingT<>编写formatter来处理缺失情况。已知C23有std::formattable概念,请问C20中有什么实现方法?


实现方案

在C++20中,我们可以通过SFINAE(替换失败不是错误)和模板重载模拟std::formattable的效果,实现TryToFormat工具,核心是判断类型T是否存在对应的std::formatter<T, char>特化。

1. 检测可格式化类型的Trait

先定义一个模板结构体,用来判断类型是否支持std::format:

#include <format>
#include <type_traits>

template <typename T, typename = void>
struct is_formattable : std::false_type {};

template <typename T>
struct is_formattable<T, std::void_t<decltype(std::formatter<T, char>{})>> : std::true_type {};

template <typename T>
inline constexpr bool is_formattable_v = is_formattable<T>::value;

这个Trait利用std::void_t检测std::formatter<T, char>能否合法实例化,以此判断类型是否支持格式化。

2. 定义降级包装类及对应Formatter

template <typename T>
struct MissingT {
    const T& value;
};

template <typename T>
struct std::formatter<MissingT<T>, char> {
    template <typename FormatContext>
    auto format(const MissingT<T>&, FormatContext& ctx) const {
        return std::format_to(ctx.out(), "(no user supplied formatter)");
    }
};

MissingT仅包装原类型的引用,对应的formatter直接输出降级文本。

3. 实现TryToFormat函数模板

通过SFINAE提供两个重载,分别处理可格式化和不可格式化的情况:

template <typename T>
auto TryToFormat(const T& value) -> std::enable_if_t<is_formattable_v<T>, const T&> {
    return value;
}

template <typename T>
auto TryToFormat(const T& value) -> std::enable_if_t<!is_formattable_v<T>, MissingT<T>> {
    return {value};
}

当T可格式化时返回原引用;否则返回MissingT<T>对象,后续std::format会自动使用降级格式化逻辑。

4. 使用示例

// 自定义无formatter的类型
struct CustomKey { int id; };

void test() {
    int int_key = 42;
    CustomKey custom_key{123};

    // 输出:The key '42' was not found
    assert(false) << std::format("The key '{}' was not found", TryToFormat(int_key));
    // 输出:The key '(no user supplied formatter)' was not found
    assert(false) << std::format("The key '{}' was not found", TryToFormat(custom_key));
}

补充说明

  • 该方案完全基于C++20标准特性,无需依赖第三方库。
  • 若需支持宽字符(wchar_t),可将Trait和formatter中的char替换为wchar_t,或进行模板化处理。
  • 注意MissingT保存的是原对象的引用,需确保原对象在格式化时仍有效,避免悬空引用问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 22:40:07