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

如何使用std::format格式化同一基类的所有派生类对象?

问题原因

std::format 匹配std::formatter特化时,会严格按照传入实参的静态类型做精确匹配,这个匹配过程不会自动触发派生类到基类的隐式引用转换。仅为Base类型编写formatter特化时,传入D1类型对象的场景下,编译器找不到对应D1类型的formatter实现,就会抛出看到的编译错误。只有显式把对象转成Base&类型传入时,参数静态类型变为Base,才能匹配到已有的特化。

正确实现方案

不要写针对Base的全特化,改为编写受约束的偏特化,让它自动匹配所有Base及其公有派生类即可,C++20环境下用std::is_base_of_v配合concept约束就能实现,不需要为每个派生类重复写特化代码。

修正后的可运行代码如下:

#include <format>
#include <iostream>
#include <string>
#include <type_traits>

using namespace std;

struct Base
{
    virtual string ToString() const
    {
        return "Base";
    }
};

struct D1 : public Base
{
    string ToString() const override
    {
        return "D1";
    }
};

// 约束:仅当T是Base或其公有派生类时,匹配该formatter特化
template <typename T, typename CharT>
requires is_base_of_v<Base, T>
struct ::std::formatter<T, CharT> : ::std::formatter<::std::string>
{
    // parse方法直接继承string的formatter实现
    template <typename FormatContext>
    auto format(const T& e, FormatContext& ctx) const
    {
        ::std::string name = e.ToString();
        return ::std::formatter<::std::string>::format(name, ctx);
    }
};

int main(int argc, char const* argv[])
{
    string s;

    D1 d;
    s = format("{}", d); // 正常编译,输出D1
    cout << s << endl;

    Base& b = d;
    s = format("{}", b); // 正常编译,输出D1
    cout << s << endl;

    return 0;
}

实现说明

  • 代码中给ToString方法加了const限定,符合const正确性:格式化接口默认接收const引用的待格式化对象,const对象无法调用非const成员函数,原代码中非const的ToString在接收const派生类对象时会触发额外编译错误。
  • 该受约束的偏特化会自动匹配所有Base的公有派生类,格式化时会通过基类虚函数自动触发多态调用,输出对应派生类ToString方法的返回值。
  • 如果使用的编译器不完全支持C++20 concept语法,可替换为SFINAE的enable_if写法,逻辑完全等价:
template <typename T, typename CharT>
struct ::std::formatter<T, CharT, enable_if_t<is_base_of_v<Base, T>, CharT>>
    : ::std::formatter<::std::string>
{
    // 内部format实现和上述代码完全一致
};

内容的提问来源于stack exchange,提问作者Martin Z He

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 19:12:29