Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is std::to_string() not templated?

As stated here std::string is not a template function but rather the standard choose to use function overloading to provide this function for different types. My question is why use overloading when template/specialisation seems to make more sense to me in this case? Consider that if the standard has defined something like this:

template <typename T>
std::string std::to_string(const T& v);

Then we can freely add specialisation for any type in our program to conform to this signature, thus C++ will have a uniform way to transform types into human-readable strings. Why not do this? What's the thinking behind the current design?

Edit 1:

The main critic I have for the current design is that adding an overload to std is not allowed so we can not write anything like std:to_string(object-that-is-of-user-defined-types) and has to fall back on defining a to_string() in their own namespace and remember where to use their version or the std version depends on the types they are dealing with... This sounds like a headache for me.

One thing I really liked about Python (or some other languages) is that you can make your own type work just like a native type by implementing some magic methods. I think what this question is fundamentally about is that why C++ decided to disallow people to implement std::to_string() for their own type and thus forbid us from conforming to the same interface everywhere.

For common things like hash or to_string(), isn't it better to have a single interface on language/stdlib level and then expect users to conform to that interface, rather than having multiple interfaces?

like image 323
Bob Fang Avatar asked Aug 30 '25 17:08

Bob Fang


1 Answers

why C++ decided to disallow people to implement std::to_string for their own type

This is where ADL is useful. We already have the example of how to correctly do this with std::swap, which is successfully done in many codebases already:

template <typename T>
void swap_example(T & a, T & b) {
    using std::swap;
    swap(a, b);
}

This works if the namespace T is declared in has a compatible swap() function, without needing to overload std::swap. We can do the same thing with std::to_string:

template <typename T>
void to_string_example(T const & x) {
    using std::to_string;
    to_string(x);
}

This will likewise work if the namespace T is declared in has a to_string function that can accept a T const & argument. For example:

namespace example {
    class Foo;

    std::string to_string(Foo const &);
}

to_string_example(example::Foo{}) would find and use the corresponding example::to_string function.


remember where to use their version or the std version depends on the types they are dealing with... This sounds like a headache for me.

If this really is such a headache for you, you can hide the ADL behind a utility function in your project:

template <typename T>
std::string adl_to_string(T const & x) {
    using std::to_string;
    return to_string(x);
}

Now you can use adl_to_string(...) instead of std::to_string(...) everywhere and not have to think about it.

like image 157
cdhowie Avatar answered Sep 02 '25 16:09

cdhowie