c++templatesfunction-templatestemplate-classes

Class vs. function template specialization


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?)


Solution

  • 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.