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

Mar*_* He 8 c++ formatting c++20 fmt

我有很多从同一个基类派生的类,并且我试图避免为所有派生类编写格式化程序。我尝试只为基类实现 std::formatter,但将派生类对象/引用传递给 std::format 将触发编译错误。

C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.32.31326\include\format(1496): 错误 C2665: 'std::_Format_arg_traits<_Context>::_Phony_basic_format_arg_constructor': 没有5 个重载可以使用 [ _Context=std::format_context ] 转换所有参数类型...

最小代码如下:

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

using namespace std;

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

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

template <typename CharT> struct ::std::formatter<Base, CharT> : ::std::formatter<::std::string>
{
    // parse is inherited from formatter<string>.
    template <typename FormatContext> auto format(Base &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); // this triggers compile errors saying no overloads found
    cout << s << endl;

    Base &b = d;
    s = format("{}", b); // ok after explicit converting to base reference
    cout << s << endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我假设编译器应该自动转换Derived&Base&,但这并没有发生。实现这一目标的正确方法是什么?

康桓瑋*_*康桓瑋 8

Base并且D1是不同的类型。更合适的方法应该是使用约束模板

#include <concepts>
#include <format>

template<std::derived_from<Base> Derived, typename CharT>
struct std::formatter<Derived, CharT> : std::formatter<std::string> {
  template<typename FormatContext>
  auto format(Derived& e, FormatContext& ctx) const {
    std::string name = e.ToString();
    return std::formatter<std::string>::format(name, ctx);
  }
};
Run Code Online (Sandbox Code Playgroud)

演示

  • 也许值得一提的是,该解决方案不需要“ToString()”是虚拟的,而是为每个派生类生成不同的实例化。如果这不是所希望的并且应该通过虚函数进行格式化,那么此解决方案应该委托给采用基类的“格式化程序”,以便编译器可以优化派生类的实例化。 (2认同)