Your "using" code is passing a template argument to toConvert(), so make sure toConvert() is actually a template, and then you can specialize it for each type you want, eg:
template<typename T>
T toConvert(std::string &input) { return T{}; /* or, throw an exception... */ }
template<>
int toConvert<int>(std::string &input)
{
return strtol(input.c_str(), 0, 10);
}
template<>
double toConvert<double>(std::string &input)
{
return strtod(input.c_str(), 0);
}
template<>
const char* toConvert<const char*>(std::string &input)
{
return input.c_str();
}
Or, if you are using C++17 or later, you can instead use if constexpr in a single template:
#include <type_traits>
template<typename T>
T toConvert(std::string &input)
{
if constexpr (std::is_same_v<T, int>)
return strtol(input.c_str(), 0, 10);
else if constexpr (std::is_same_v<T, double>)
return strtod(input.c_str(), 0);
else if constexpr (std::is_same_v<T, const char*>)
return input.c_str();
else
return T{}; // or, throw an exception, or static_assert a compiler error...
}
Notice in either case that I changed the input parameter to std::string&. Your original code was taking in the std::string by value, which means it takes in a copy of the caller's string, and so the const char* conversion would return a dangling pointer to invalid memory when the copied std::string is freed upon return. By taking a reference instead, you avoid that copy.
You might be tempted to take in a const std::string& reference instead, but that would allow calls like toConvert<const char*>("string") to compile but still return a dangling pointer, since the compiler would have to create a temporary std::string to bind to the const reference. But a string literal can't bind to a non-const reference.