Chris_F
Chris_F

Reputation: 5557

How could I optimise this simple function?

Lets say I have the following:

template <class T>
std::string to_string(const T& item)
{
    std::ostringstream ss;
    ss << item;
    return ss.str();
}

Is it possible to create an overload of to_string that in the case of being given a std::string would simply forward the argument without any overhead?

Upvotes: 1

Views: 130

Answers (4)

Mark Garcia
Mark Garcia

Reputation: 17708

Is it possible to create an overload of to_string that in the case of being given a std::string ...

Of course.

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

... would simply forward the argument without any overhead?

Not sure what you mean by "forward" here, but...

You could provide another overload on cases where a temporary or std::moved object is provided as an argument to to_string. Use an r-value reference for that.

std::string to_string(std::string&& str) {
   return std::move(str);
}

Note that the std::move at the return statement is necessary here, as it would result into a copy if it is not used (the compiler treats all named variables as lvalues, and with str a named variable, there's the need for std::move).

Now you have two versions overloaded for std::string the first will effectively return a copy of the provided argument, while the second one takes advantage temporary objects and explicitly std::moved objects.

std::string str = "Hello World!";
auto str1 = to_string(str);    // *effectively* copies str to str1 [1]
auto str2 = to_string("Hi");   // *effectively* moves the temporary created here [1]
auto str3 = to_string(std::move(str));   // *effectively* moves str to str3; [1]

[1] Actually, there still may be several additional moves involved when returning, and copy-elision optimizations may be applied by the compiler on some cases.


UPDATE

Wouldn't auto str2 = to_string("Hi"); call the template function, as that does not involve an implicit conversion and is therefor the more suitable overload? link

You're right. With that, you could provide another overload accepting a const char*

std::string to_string(const char* str) {
   return str;
}

or you could document the behavior and let (more like force) users to do to_string(std::string("bah")). I'm pretty sure you'll provide the overload though :-).

Upvotes: 4

Abhishek Mittal
Abhishek Mittal

Reputation: 366

If I have understood your problem correctly, then you are asking for "Template Specialization".

template <class T>
std::string to_string(const T& item)
{
    std::ostringstream ss;
    ss << item;
    return ss.str();
}

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

IMPORTANT NOTE:

In the to_string function we have passed "const string&", therefore function call should be like:

string my_string = "ABC";
to_string (my_string);

to call the specialized function.

If you call to_string like:

to_string ("ABC");

then "std::string to_string(const T& item)" general function would be called instead of specialized function.

I hope that this will solve your query.

Upvotes: 0

Praetorian
Praetorian

Reputation: 109089

I'd add a couple of overloads, one that returns a copy of an lvalue std::string and another that moves an rvalue std::string.

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

std::string to_string(std::string&& item)
{
    return std::move(item);
}

std::string s("Hello, World!");
std::cout << to_string(s) << std::endl;  // calls the lvalue overload

std::cout << to_string(std::string("Hello, World!")) << std::endl; // calls the rvalue overload
std::cout << to_string(20) << std::endl; // calls the function template

Live demo


You could also let std::to_string do the job for cases where T is numeric by tag dispatching to std::to_string using std::is_arithmetic<T>.

template <class T>
std::string to_string_impl(const T& item, std::true_type)
{
    return std::to_string(item);
}

template <class T>
std::string to_string_impl(const T& item, std::false_type)
{
    std::ostringstream ss;
    ss << item;
    return ss.str();
}

template <class T>
std::string to_string(const T& item)
{
    return to_string_impl(item, std::is_arithmetic<T>{});
}

Live demo


Finally, if you can use boost, your function can be replaced by boost::lexical_cast.

auto str = boost::lexical_cast<std::string>(whatever);

Upvotes: 6

Ylisar
Ylisar

Reputation: 4293

If you care about performance, stay away from any stringstream stream and its ilks, they're sadly incredibly slow. You're correct that you could just introduce an overload such as std::string to_string( const std::string& item ), and it would be a better match and get selected. There's also boost::lexical_cast, and perhaps something similar in c++11 ( someone else will have to chip in here ). boost::lexical_cast however is also very slow due to its internals relying on stringstream. IMO the best way is to introduce your own function, such as you've done with your to_string, fallback to boost::lexical_cast and specialize / overload for your own types. We specialize a lot of our types using boost::spirit::qi, there's performance comparisons out there which rings true with our tests, it's rather easy to use boost::spirit::qi and its parsers to get way better performance characteristics than stringstream.

Upvotes: 0

Related Questions