Tyler Lewis Tyler Lewis - 3 months ago 9
C++ Question

Why does a std::string object passed to a template function not prefer std::string overload?

I'm writing a program and I want to be able to cleanly wrap a string in quotes without having to do something like

std::string firstString = "This is a string";
std::string myString = "\"" + firstString + "\"";


So I wrote a couple of template functions to take their arguments and wrap them in quotes. I've also included my first (naive) attempt at writing a general toString() function (I know about to_string, but I'm doing this for learning too).

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <typeinfo>

template <typename T>
std::string toString(const T &convert)
{
std::string returnString{""};
std::stringstream transfer;
transfer << convert;
transfer >> returnString;
return returnString;
}

template<typename T>
std::string tQuoted(const T &convert)
{
std::cout << "Called template overload" << std::endl;
return ("\"" + toString(convert) + "\"");
}

template<typename T>
std::string tQuoted(const std::string &convert)
{
std::cout << "Called std::string overload" << std::endl;
return ("\"" + convert + "\"");
}

template<typename T>
std::string tQuoted(const char *convert)
{
std::cout << "Called const char overload" << std::endl;
return ("\"" + static_cast<std::string>(convert) + "\"");
}

template<typename T>
std::string tQuoted(std::string convert)
{
std::cout << "Called normal std::string overload" << std::endl;
return ("\"" + convert + "\"");
}

template<typename T>
std::string tQuoted(std::string&& convert)
{
std::cout << "Called rvalue std::string overload" << std::endl;
return ("\"" + convert + "\"");
}

int main()
{
std::vector<std::string> my{"Hello", "30 Days Of Coding", "All Work And No Play"};

std::string myString = "Hello, World!";
std::string *strPtr = &myString;
std::string *mySuperPtr = new std::string{"He's a cockaroach"};

for (std::vector<std::string>::const_iterator iter = my.begin(); iter != my.end(); iter++) {
std::cout << tQuoted(*iter) << std::endl;
}

std::cout << tQuoted(myString) << std::endl;
std::cout << tQuoted(*strPtr) << std::endl;
std::cout << tQuoted(mySuperPtr) << std::endl;
std::cout << tQuoted(std::string{"Another string"}) << std::endl;

delete mySuperPtr;
mySuperPtr = nullptr;

return 0;

}


Every one of those calls the template constructor:

Called template overload
"Hello"
Called template overload
"30"
Called template overload
"All"
Called template overload
"Hello,"
Called template overload
"Hello,"
Called template overload
"0x13cad10"
Called template overload
"Another"


Of course, a much less naive toString() method would do basic checking to see if the parameter was a std::string, and just return that if it was. It seems that a std::stringstream will stop when it encounters the first space in a string (hence the truncated output). However, this isn't the main focus of my confusion.

Sorry for the very basic question, but this one really has me stumped. Thanks for any help you can provide.

Answer

You are not specializing the template function correctly. This is how to correctly specialize it:

template<>
std::string tQuoted(const std::string &convert)
{
    std::cout << "Called std::string overload" << std::endl;
    return ("\"" + convert + "\"");
}

Resulting output becomes:

Called std::string overload
"Hello"
Called std::string overload
"30 Days Of Coding"
Called std::string overload
"All Work And No Play"
Called std::string overload
"Hello, World!"
Called std::string overload
"Hello, World!"
Called template overload
"0x1c27d10"
Called std::string overload
"Another string"

Note that for

tQuoted(mySuperPtr) 

mySuperPtr is a pointer to a string, and not a string, hence this doesn't use the specialized template function.

Comments