tags:

views:

541

answers:

4

Can you specialize a template method within a template class without specializing the class template parameter?

Please note that the specialization is on the value of the template parameter, not its type.

This seems to compile under Visual Studio 2008 SP1 complier, but not GCC 4.2.4.

#include <iostream>
using namespace std;
template <typename T>
class A
{
private:
    template <bool b>
    void testme();

    template <>
    void testme<true>() { cout << "true" << endl; };

    template <>
    void testme<false>() { cout << "false" << endl; };

public:
    void test();
};

template<typename T> struct select {};
template<> struct select<int>    { static const bool value = true; };
template<> struct select<double> { static const bool value = false; };

template <class T>
void A<T>::test() { testme<select<T>::value>(); }

int main(int argc, const char* argv[])
{
    A<int>      aInt;
    A<double>   aDouble;

    aInt.test();
    aDouble.test();

    return 0;
}

GCC tells me:"error: explicit specialization in non-namespace scope ‘class A’"

If it is not supported in the standard, can anyone tell me why?

+4  A: 

It is not supported in the standard (and it is apparently a known bug with Visual Studio that you can do it).

The standard doesn't allow an inner template (member function or class) to be specialized without the outer template being specialized as well. One of the reasons for this is that you can normally just overload the function:

template<typename ty>
class A
{
public:
      void foo(bool b);
      void foo(int i);
};

Is equivalent to:

template<typename ty>
class A
{
public:
   template<typename ty2>
   void foo(ty2);

   template<>
   void foo(bool b);

   template<>
   void foo(int i);
};
mos
This makes scene. However, our example specializes on the *value* of the template parameter, not its type.
Diederik
A: 

I've never heard of that being possible; it would make sense to me if it was not supported by all compilers. So here is an idea for a workaround:

Implement a template function outside of your class which takes the same action as the method. Then you can specialize this function, and it call it from the method. Of course, you'll also have to pass in any member variables that it needs (and pointers thereto if you want to modify their values).

You could also create another template class as a subclass, and specialize that one, although I've never done this myself and am not 100% sure it would work. (Please comment to augment this answer if you know whether or not this second approach would work!)

Tyler
+1  A: 

Here is another workaround, also useful when you need to partialy specialize a function (which is not allowed). Create a template functor class (ie. class whose sole purpose is to execute a single member function, usually named operator() ), specialize it and then call from within your template function.

I think I learned this trick from Herb Sutter, but do not remember which book (or article) was that. For your needs it is probably overkill, but nonetheless ...

template <typename T>
struct select;

template <bool B>
struct testme_helper
{
  void operator()();
};

template <typename T>
class A
{
private:
  template <bool B> void testme()
  {
    testme_helper<B>()();
  }

public:
  void test()
  {
    testme<select<T>::value>();
  }
};

template<> void testme_helper<true>::operator()()
{
  std::cout << "true" << std::endl;
}

template<> void testme_helper<false>::operator()()
{
  std::cout << "false" << std::endl;
}
This is the solution we came up with too. Thanks for the answer.
Diederik
+2  A: 

here is how you do it:

template<typename A>
struct SomeTempl {
    template<bool C> typename enable_if<C>::type 
    SomeOtherTempl() {
        std::cout << "true!";
    }

    template<bool C> typename enable_if<!C>::type 
    SomeOtherTempl() {
        std::cout << "false!";
    }
};

You can get enable_if from my other answer where i told them how to check for a member function's existance in a class using templates. or you can use boost, but remember to change enable_if to enable_if_c then.

Johannes Schaub - litb