lawilog lawilog - 1 month ago 8
C++ Question

Templated function template specialization

I want to write a specialization for a template function, where the type for which it is specialized is itself a templated type. (I am using C++11 or higher.)

In the example code below, I have the generic function

convertTo
and a working specialization for
int
, allowing me to use
convertTo<int>(s)
(as shown). But I cannot figure out how to write specializations for, e.g.,
std::set<T>
. This is what I tried:

#include <string>
#include <sstream>
#include <set>
#include <unordered_set>
using namespace std;

// generic version
template<class T> T convertTo(const char* str) {
T output;
stringstream ss(str, stringstream::in);
ss >> output;
return output;
}

// specialization for int. works.
template <>
int convertTo<int>(const char* str) {
return atoi(str);
}

template <>
template<class T> set<T> convertTo<set<T>>(const char* str) {
set<T> S;
// TODO split str by comma, convertTo<T>(each element) and put into S
return S;
}

template <>
template<class T> unordered_set<T> convertTo<unordered_set<T>>(const char* str) {
unordered_set<T> S;
// TODO split str by comma, convertTo<T>(each element) and put into S
return S;
}

int main() {
float f = convertTo<float>("3.141");
int i = convertTo<int>("123");
set<int> os = convertTo<set<int>>("9,8,7,6");
unordered_set<int> os = convertTo<unordered_set<int>>("9,8,7,6");
return 0;
}


With g++ 6.3.0 I get the error message:

too many template parameter lists in declaration of ‘std::set<T> convertTo(const char*)’


So I tried to comment out the lines
template<>
above the attempted specializations, but then I get:

non-class, non-variable partial specialization ‘convertTo<std::set<T, std::less<_Key>, std::allocator<_CharT> > >’ is not allowed


I don't understand. I didn't intend to write a partial specialization?

I do not want to use
template<class Container>
, because I want to be able to write specific code for different container classes. (I need this for other template classes in my code.)

Any advise on how to do this?

Answer Source

A partial specialization is when you specify part of the type of the template, but not the whole thing. For example,

template <>
template<class T> set<T> convertTo<set<T>>(const char* str)

would partially specialize for set<T> if partial function specialization were allowed.

The two main ways of handling this are to instead overload, by removing the template<> part, or better yet switch to using template class specialization. The problem with overloading is that it looks somewhat different if you are overloading with another template (as with set<T>) or with a single type (as with int), and mixing specialization and overload almost certainly doesn't work as you expect.

Therefore, template class specialization is usually the best way to go, and can be done like this:

// Generic version
template <typename T>
class Converter {
  public:
    T operator() (const char* str) {
          T output;
          stringstream ss(str, stringstream::in);
          ss >> output;
          return output;
    }
};

template <>
class Converter<int> {
  public:
    int operator() (const char* str) {
        return atoi(str);
    }
};

// ...

template <typename T>
T convertTo(const char* str) {
    return Converter<T>{}(str);
}

That way you can use any type of specialization you want for the class without issue.