I only recently learned about partial template specialization in C++ from here, and it perfectly solved a problem where I needed a template class to behave differently for pointers and non-pointers. A simplified example is:
// main.cpp
template <typename T>
class C
{
public:
C( const T& t ) : t_( t ) {}
private:
T t_;
};
template <typename T>
class C<T*>
{
public:
C( const T& t ) : t_( new T(t) ) {}
~C() { delete t_; }
private:
T* t_;
};
int main( int argc, char* argv[] )
{
C<int> c1(4);
C<int*> c2(2);
return 0;
}
I wanted to try extend this idea to functions, but ran into compiler errors I didn't understand:
// main.cpp
#include <set>
template <typename T>
std::set<T> makeSet( size_t off )
{
std::set<T> s;
s.insert( T() + T(off) );
return s;
}
template <typename T>
std::set<T*> makeSet<T*>( size_t off )
{
std::set<T*> s;
T* t = new T( T() + T(off) );
s.insert( t );
return s;
}
int main( int argc, char* argv[] )
{
std::set<int> s1 = makeSet<int>(4);
std::set<int*> s2 = makeSet<int*>(2);
delete *(s2.begin());
return 0;
}
.
$ g++ --version && g++ -g ./main.cpp
g++ (GCC) 9.2.1 20190827 (Red Hat 9.2.1-1)
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
./main.cpp:13:38: error: non-class, non-variable partial specialization ‘makeSet<T*>’ is not allowed
13 | std::set<T*> makeSet<T*>( size_t off )
| ^
./main.cpp: In function ‘int main(int, char**)’:
./main.cpp:23:37: error: call of overloaded ‘makeSet<int>(int)’ is ambiguous
23 | std::set<int> s1 = makeSet<int>(4);
| ^
./main.cpp:5:13: note: candidate: ‘std::set<T> makeSet(size_t) [with T = int; size_t = long unsigned int]’
5 | std::set<T> makeSet( size_t off )
| ^~~~~~~
./main.cpp:13:14: note: candidate: ‘std::set<T*> makeSet(size_t) [with T = int; size_t = long unsigned int]’
13 | std::set<T*> makeSet<T*>( size_t off )
| ^~~~~~~~~~~
./main.cpp:24:38: error: call of overloaded ‘makeSet<int*>(int)’ is ambiguous
24 | std::set<int*> s2 = makeSet<int*>(2);
| ^
./main.cpp:5:13: note: candidate: ‘std::set<T> makeSet(size_t) [with T = int*; size_t = long unsigned int]’
5 | std::set<T> makeSet( size_t off )
| ^~~~~~~
./main.cpp:13:14: note: candidate: ‘std::set<T*> makeSet(size_t) [with T = int*; size_t = long unsigned int]’
13 | std::set<T*> makeSet<T*>( size_t off )
| ^~~~~~~~~~~
I was a little skeptical about this because it looks like an attempt at overloading a function with a different return-type but with the same arguments - I know this is otherwise illegal, but I thought that because these were template functions, specifying the template type when invoking the functions (as in the above main()
) would allow disambiguation.
But I'm not sure that's what the compiler is complaining about on line 13: what does the non-class, non-variable partial specialization ‘makeSet<T*>’ is not allowed
error mean?
Is what I'm trying to do even possible in C++? I.e. can a template function be made so that it behaves differently for pointers and non-pointer template types and return a STL container of the specified type? (Would the answer be different between C++ 98, 03, 11, and 14?)
what does the
non-class, non-variable partial specialization ‘makeSet<T*>’ is not allowed
error mean?
Let's take a look at some documentation from cppreference.com regarding partial template specialization:
Allows customizing class [and variable (since C++14)] templates for a given category of template arguments.
Partial specializations are allowed for class and variable templates. The compiler has told you that your template is for neither a class for a variable. Therefore, partial specialization is not allowed.
A less confusing wording for the compiler's message would be: partial specialization of function templates is not allowed.
As for what can be done, it is possible to declare a class template with a suitable operator()
. In some cases this is about as good as using a function template.