Making a user-defined class std::to_string(able)
What's the 'best' way is an open question.
There are a few ways.
The first thing to say is that overloading std::to_string
for a custom type is not allowed. We may only specialise template functions and classes in the std
namespace for custom types, and std::to_string
is not a template function.
That said, a good way to treat to_string
is much like an operator or an implementation of swap
. i.e. allow argument-dependent-lookup to do the work.
so when we want to convert something to a string we could write:
using std::to_string;
auto s = to_string(x) + " : " + to_string(i);
assuming that x was an object of type X in namespace Y and i was an int, we could then define:
namespace Y {
std::string to_string(const X& x);
}
which would now mean that:
invoking to_string(x)
actually selects Y::to_string(const Y::X&)
, and
invoking to_string(i)
selects std::to_string(int)
Going further, it may be that you want to_string to do much the same as operator<<, so then one can be written in terms of the other:
namespace Y {
inline std::ostream& operator<<(std::ostream& os, const X& x) { /* implement here */; return os; }
inline std::string to_string(const X& x) {
std::ostringstream ss;
ss << x;
return ss.str();
}
}
First, some ADL helping:
namespace notstd {
namespace adl_helper {
using std::to_string;
template<class T>
std::string as_string( T&& t ) {
return to_string( std::forward<T>(t) );
}
}
template<class T>
std::string to_string( T&& t ) {
return adl_helper::as_string(std::forward<T>(t));
}
}
notstd::to_string(blah)
will do an ADL-lookup of to_string(blah)
with std::to_string
in scope.
We then modify your class:
class my_class{
public:
friend std::string to_string(my_class const& self) {
return "I am " + notstd::to_string(self.i);
}
int i;
};
and now notstd::to_string(my_object)
finds the proper to_string
, as does notstd::to_string(7)
.
With a touch more work, we can even support .tostring()
methods on types to be auto-detected and used.
You could define your own to_string
in its own namespace (e.g., foo
).
namespace foo {
std::string to_string(my_class const &obj) {
return obj.string give_me_a_string_of_you();
}
}
And use it as:
int main(){
my_class my_object;
std::cout<< foo::to_string(my_object);
}
Unfortunatelly, you can't define your own version of to_string
in namespace std
because acorrding to the standard 17.6.4.2.1 Namespace std [namespace.std] (Emphasis Mine):
The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.