Bob Fang
Bob Fang

Reputation: 7381

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?

Upvotes: 4

Views: 1076

Answers (2)

cdhowie
cdhowie

Reputation: 169028

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.

Upvotes: 5

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122516

This may sound a bit boring, but the purpose of std::to_string is to format as if you had used sprintf and as sprintf supports only a limited set of types this is also true for std::to_string. There is no need to make it a template.

As explained in detail in this answer that design does not have the restriction you think it has. You can still supply your own foo::to_string(foo::bar&) and in code that uses proper qualification of the name to enable ADL your overload will be called. For this it is not necessary to add an overload to std.

Upvotes: 2

Related Questions