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?
For whatever reason a common answer for this problem is to forward to a static method of an implementation struct. A struct can be partially specialized so this does solve the issue. But it's usually better to use overloading; there are various reasons for that but here I'll simply restrict myself to saying that it's less boilerplate per implementation. You can do it like this:
template <class T>
struct tag{}; // implementation detail
template<class T>
T convertTo(const char* str, tag<T>) {
T output;
stringstream ss(str, stringstream::in);
ss >> output;
return output;
}
int convertTo(const char* str, tag<int>) {
return atoi(str);
}
template<class T>
set<T> convertTo(const char* str, tag<set<T>>) {
set<T> S;
// TODO split str by comma, convertTo<T>(each element) and put into S
return S;
}
template<class T>
unordered_set<T> convertTo(const char* str, tag<unordered_set<T>>) {
unordered_set<T> S;
// TODO split str by comma, convertTo<T>(each element) and put into S
return S;
}
template <class T>
T convertTo(const char * s) {
return convertTo(s, tag<T>{});
};
All of the convertTo
taking two arguments are just overloads now, which is fine. The user facing convertTo
simply calls to the two argument form using our little tag struct to control the dispatch in exactly the same way as partial specialization.
There's lots of other interesting merits to this technique, like the ability to control overload resolution more precisely using additional tag
-like structs and derived-base conversion, the utility of ADL/2-phase lookup, but it's a bit out of scope here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With