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.