I have lots of classes derived from the same base class, and I'm trying to avoid writing a formatter for all derived classes. I tried to only implement the std::formatter for the base class, but passing a derived class object/reference to std::format will trigger compile errors.
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.32.31326\include\format(1496): error C2665: 'std::_Format_arg_traits<_Context>::_Phony_basic_format_arg_constructor': none of the 5 overloads could convert all the argument types with [ _Context=std::format_context ] ...
Minimal code is as follows:
#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;
}
I'm assuming that the compiler should automatically convert Derived&
to Base&
, but that doesn't happen. What's the correct way to achieve this?
ToString()
is virtual, but produces a different instantiation for every derived class. If that is not desired and formatting should happen via the virtual function, then this solution should delegate to aformatter
that takes the base class so that the compiler can optimize away the instantiations for the derived classes. – Emma