Reputation: 7381
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
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
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