My personal rule:
Whenever I define a type, a class, or
whatever, I define either a print function, or a to_string function.
class Foo {…public: std::string to_string() const { std::string ret; ret += “”;”; ret += fmt(“field1=%d”,this->field1); … ret += “
return ret; } // or public: friend std::ostream& operator<< (std::ostream& ostream, const Foo& this_value) { ostream << “
return ostream; } }
Sometimes I have to_string_compact(),
to_string_verbose(), etc;
Unfortunately, it is harder to express that
with the osttream operator<< syntax.
But the operator<< syntax can be more
efficient – less allocation of temporary strings.
And the formatting can be easier.
--
The main reason why I don't use the operator<< everywhere is that I have rn into far too many broken ostreams. not so common any more, but was common for a while.
--
I have several times written tools to automatically generate these.
--
It is straightforward to generate to_string from operator<<, and vice versa. Since operator<< can be more efficient, usually best to do the former. But some ostreams can be inefficient.
--
Rarely you may want to make the operator<< a template, accepting an OSTREAM type - because I have run into libraries that look like streams, but which are not in the same type hierarchy, and which do not inherit from std::ostream.