Search code examples
c++templatesdependent-name

Specializing member function for non-template class in C++


I'm trying to specialize a template member function of a non-template class using a templatized parameter:

#include <array>
class C
{
public:
  template<class Container>
  void Foo( Container& )
  {
    // ...
  }
};

template<class T, std::size_t N>
template<>
void C::Foo< std::tr1::array<T,N> >( std::tr1::array<T,N>& )
{
  // special
}

I get an error "illegal use of explicit template arguments" with this. What's the right syntax to make this valid?


Update:

Perhaps I've muddled the issue by over-simplifying. What I really want to do is specially handle this one case where there is a dependent name involved, which I think may be what is throwing a monkey wrench into the works here. My original thought was to overload the function as such:

class C
{
public:
  template<class Iter>
  void Foo( Iter )
  {
    std::cout << "Normal\n";
  }

  template<class T, std::size_t N>
  void Foo( typename std::tr1::array<T,N>::iterator )
  {
    std::cout << "Special\n";
  }
};

int main()
{
  C c;
  std::tr1::array<int,10> a1;
  c.Foo( a1.begin() ); // Doesn't print "Special"!
}

But the special Foo doesn't get called. How can I do that?


Solution

  • Only the member function is templated, which means that you should only use one template<...> in there. But that will not solve it either, as you cannot partially specialize a function.

    The usual way of handling the problem is through overloads, rather than specialization (specialization of template functions is not that useful).

    struct test {
       template <typename Container>
       void f( Container& ) { ... }
    
       template <typename T, int N>
       void f( std::array<T,N>& ) { ... }
    };
    

    Note that the difference is that they are two separate template functions (rather than an specialization).

    EDIT: After the update

    The update to the question changes the problem completely. The problem that you are seeing is that the argument of the second version is a dependent name, and as such it is non deducible. Given the function call the compiler is not able to determine what type T, and integral constant N are to match that particular instantiation. Consider a different example:

    template <typename T>
    struct inner_int {
       typedef int type;
    };
    template <typename T>
    void foo( typename inner_int<T>::type ) {
    }
    int main() {
       foo( inner_int<double>::type() );
    }
    

    When the compiler processes the call in main, it instantiates the template and extracts the type, from that it creates a temporary, and then it tries to decide what to do with foo but at that time it only knows that it is being called with an int rvalue... the original inner<double>::type is gone, now it is just foo( int() ) and the compiler would have to try and instantiate inner_int with all possible types to determine whether any of them fits, and in the worse case, as above, many would match.