8

I have template code which needs to convert some template type to string. For this I overload to_string for my own types. But the type can also be a string already. Then compilation fails, because there is no overload of to_string for type string itself (just returning its argument).

edit: example code:

template<class T>
class TemplatedClass
{
public:

string toString() const
{
    // this should work for both simple types like int, double, ...
    // and for my own classes which have a to_string overload
    // and also for string, which is the reason for my question
    return string("TemplatedClass: ").append(to_string(t_));
}

private:
    T t_;
};
10
  • 2
    Are you asking us how to solve your problem or why there is no overload you are looking for? For former, please provide MCVE. For latter, it's easy to answer - because such overload would be ridiculous. Commented May 19, 2016 at 13:57
  • Write it yourself? Also, it would be "better" to use some operator<< or other serialization method that is equally suited for this but more "idiomatic". Commented May 19, 2016 at 13:57
  • 4
    Also, you need to be careful how you overload to_string. Putting your overloads in the std namespace is undefined behaviour. Commented May 19, 2016 at 14:01
  • 2
    @mgr I think they should go in the same namespace where the type they convert is defined. When you call to_string(x) do using std::to_string before and the compiler will either use the overload from std or the one you provided by ADL. Commented May 20, 2016 at 7:05
  • 1
    @mgr No, you almost never put something in namespace standard (there are a few defined exceptions). What you should do is to put types and free functions into the same namespace. For std::string, this is of course not possible. In your case, I would make to_string( std::string const& ) a member function of TemplatedClass. Or specialize the class for std::string. Commented May 20, 2016 at 9:11

3 Answers 3

9

You can just write your own templated function with proper overloads as follows:

#include <iostream>
#include <string>
using namespace std;

template<typename T>
std::string toString(const T& t) {
    return std::to_string(t);
}

std::string toString(const char* t) {
    return t;
}

std::string toString(const std::string& t) {
    return t;
}


int main() {
    cout << toString(10) << endl;
    cout << toString(1.5) << endl;
    cout << toString("char*") << endl;
    cout << toString(std::string("string")) << endl;
    return 0;
}
Sign up to request clarification or add additional context in comments.

5 Comments

@SebastianHoffmann There's no SFINAE in this. Where do you see a substitution failure?
@T.C. The template instantiation for std::string won't yield an error?
@SebastianHoffmann No, it won't. Substitution will succeed, and then the template won't be instantiated because the non-template overload is a better match.
@mgr It'll compile. Just read the previous comments to see why. Here you can check it too.
@EissaN. I stand corrected. My compiler error was a bit long and convoluted (as usual with templates...) and had another root cause.
6

You can just combine all std::to_string and all your to_string by using derective. And pass std::string by value to minimize number of copies:

#include <string>
#include <iostream>

namespace convert {
    std::string to_string(std::string s)
    {
        return s;
    }

    template<class T>
    std::string stringify(T&& t)
    {
        using convert::to_string;
        using std::to_string;
        return to_string(std::forward<T>(t));
    }
}

class Foo
{
public:
    operator std::string () const { return "Foo"; }
};

namespace bar {
    class Bar
    {};

    std::string to_string(const Bar&) {
        return "Bar";
    }
}

int main()
{
    std::string s{"I'm lvalue string"};
    std::cout << convert::stringify(42) << "\n";
    std::cout << convert::stringify(std::string("I'm string")) << "\n";
    std::cout << convert::stringify("I'm c-string") << "\n";
    std::cout << convert::stringify(s) << "\n";
    std::cout << convert::stringify(Foo{}) << "\n";
    std::cout << convert::stringify(bar::Bar{}) << "\n";

    return 0;
}

Note that with my approach you don't need an overload for const char * or any other type that is convertible to a string. Also this approach allows a user to add to_string overload for any class (it will be found by argument-dependent lookup).

For further optimization convert::to_string accepting a string by value can be split into lvalue and rvalue overloads.

2 Comments

Only suggestion (and it may be entirely invalid depending on the use case) would be to change the input/output to use perfect forwarding of references and then return those references. If s is large, then there are no allocations and/or the caller doesn't have to worry about moving the string. template<class Ch, class Tr, class Alloc> auto to_string(std::basic_string<Ch, Tr, Alloc> &&s) -> decltype(std::forward<std::basic_string<Ch, Tr, Alloc>>(s)) { return s; }
@Andrew yep, that's an option although I don't really like the duality of the contract then. because generic to_string may return either a value or a ref. Btw s should be forwarded in the return expression. And the auto for the return means that return is done by value. It should be decltype(auto) instead.
0

Since C++17 you can use constexpr in if statements.

You can use it as follows:

#include <iostream>
#include <string>

template<typename Ty>
std::string ToString(const Ty& value) {
    if constexpr (std::is_convertible_v<Ty, std::string>) {
        return value;
    }
    else {
        return std::to_string(value);
    }
}

int main() {
    std::cout << ToString(123) << std::endl;
    std::cout << ToString(123.0) << std::endl;
    std::cout << ToString("char*") << std::endl;
    std::cout << ToString(std::string("std::string")) << std::endl;

    return 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.