8

There is the template class List.

template <typename Point>
class List
{


    public:
          template <const unsigned short N>
          void load ( const char *file);
          ...
};

template <typename Point>
template <const unsigned short N>
void List <Point>::load ( const char *file)
}

How to specialize method load for N=2? This code is not valid...

template <typename Point>
void List <Point> <2>::load ( const char *file)
{
}

And this code also does not work.

template <typename Point>
void List <Point> ::load <2> ( const char *file )
{ 
}

Error 3 error C2768: 'List<Point>::load' : illegal use of explicit template arguments 66. 
Error 5 error C2244: 'List<Point>::load' : unable to match function definition to an existing declaration 66

Compiler g++:

template <typename Point>
template <>
void List <Point> ::load <2> ( const char *file )
{
}

error: explicit specialization in non-namespace scope `class List<>'
error: enclosing class templates are not explicitly specialized
error: default arguments are only permitted for function parameters
error: `load' is not a function template
error: invalid function declaration
einpoklum
  • 118,144
  • 57
  • 340
  • 684
Robo
  • 311
  • 5
  • 10
  • I am sorry, there was error in code... So I updated my question. – Robo Feb 09 '11 at 11:28
  • 1
    http://stackoverflow.com/questions/734797/template-specialization-of-function-inside-of-a-templated-class/734818#734818 – Shelwien Feb 09 '11 at 14:41
  • @Shelwien... Thanks for the link. But I am not entirely clear how to use it... Could you give me a short example, please? – Robo Feb 09 '11 at 21:46

3 Answers3

9

It turns out that there's a provision in the C++ spec that explicitly disallows specializing a template class or function nested inside of a template class unless you also explicitly specialize the outer template as well. Visual Studio doesn't enforce this rule, hence the confusion with the previous example, but g++ certainly does.

If you want to specialize the template, your options will either be to also specialize the outer template or to somehow fake up the behavior of specialization by having the method dispatch to one of two different implementations based on the template parameter. Neither of these are very satisfying, I know, but unfortunately the language is designed weirdly in some template corners. :-(

One way that you can emulate the behavior of the explicit specialization is to use a technique called tag dispatching. The idea is that we'll make a very simple struct that looks like this:

template <unsigned short N> struct Box {};

This type is completely empty. It's not meant to be used directly, but rather is just a way of embedding an integer into the type system. In particular, Box<3> is not the same type as Box<4>, etc.

Next, in your list class, define two functions that look like this, preferably marked private:

template <unsigned short N>
    void doLoad(const char* file, Box<N>);
void doLoad(const char* file, Box<2>);

These two functions are overloads of one another, distinguishable only by their final parameter, which is either a Box<N> in the template case or a Box<2> in the non-template case. Note that the parameters don't have names. This is an arbitrary decision, but since we're not planning on actually reading the parameters, we don't need them. The intuition behind these functions is that this first function will be the "catch-all" implementation that will work for any N except 2. The second version will contain the implementation of loading for the case where N == 2.

Finally, implement load as follows:

template <typename Point>
    template <unsigned short N>
void List<Point>::load(const char* file) {
    doLoad(file, Box<N>());
}

How does this work? This function takes in a parameter, and then calls doLoad forwarding that parameter as the first argument and passing a temporary Box<N> as the second argument. If N is not two, then this is a call to the template version of doLoad, which is the catch-all handler. If, on the other hand, N is two, then this will call the non-template version of doLoad, because non-template functions have priority over template functions during overload resolution.

In short, the implementation of load just becomes a trampoline to forward you to the correct of the two implementations. You can then put the logic in the appropriate doLoad function to get the behavior you want.

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
1

Edit: Ok, so I rewrote your class a bit, with inlined function definitions, and this definitely works:

template <typename Point>
class List
{
public:
    template <const unsigned short N>
    void load( const char *file){
    }

    template<>
    void load<2>(const char* file){
    }
};
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • But it does not work: Error 3 error C2768: 'List::load' : illegal use of explicit template arguments 66. Error 5 error C2244: 'List::load' : unable to match function definition to an existing declaration 66 – Robo Feb 09 '11 at 11:45
  • Ah, one error is in your function definition of `load`. It should be `template List::Load(char const* file){...}`. Lemme check the rest quickly... – Xeo Feb 09 '11 at 11:48
  • template template <> void List ::load <2> ( const char *file ) { } does not work... – Robo Feb 09 '11 at 12:34
  • @Robo: Do it exactly as I do in my answer, not outside of the class. – Xeo Feb 09 '11 at 12:46
  • 2
    @Xeo: Unfortunately, IIRC explicit specialization in class definition isn't allowed in the standard, though MSVC allows it as its extension probably. – Ise Wisteria Feb 09 '11 at 13:38
  • @Ise: Oh, I see... that's too bad then, sorry. – Xeo Feb 09 '11 at 15:34
  • @Xeo: Oh, there was no intent to blame you in my comment :-) – Ise Wisteria Feb 09 '11 at 16:54
1

You cannot specialize a member template without also specializing the class template.

I also wonder what the meaning of N could be, as it is not used in the function parameter?

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • I need to decide which function will be used at runtime not at compile time. – Robo Feb 09 '11 at 22:11
  • 1
    But templates are instantiated at compile time, not runtime. For runtime polymorphism you use virtual functions. Doesn't work for values though - perhaps a switch statement would do? – Bo Persson Feb 09 '11 at 22:27