20

I'm playing around with template specialization, and I've found an issue I can't seem to solve; this is my code:

template<int length, typename T>
void test(T* array)
{
    ...
    test<length-1>(array);
}

template<typename T>
void test<0>(T* array)
{
    return;
}

So what I'm trying to do, is to pass the length, of what's to be processed in the template.

The problem is, that the compilation of this, well outputs forever:

a.cpp:83:43: error: template-id 'test<0>' in declaration of primary template
a.cpp: In function 'void test(T*) [with int length= -0x000000081, T = int]':
a.cpp:77:9:   instantiated from 'void test(T*) [with int length= -0x000000080, T = int]'
a.cpp:77:9:   instantiated from 'void test(T*) [with int length= -0x00000007f, T = int]'
a.cpp:77:9:   [ skipping 151 instantiation contexts ]
a.cpp:77:9:   instantiated from 'void test(T*) [with int length= 28, T = int]'
a.cpp:77:9:   instantiated from 'void test(T*) [with int length= 29, T = int]'
...
a.cpp: In function 'void test(T*) [with int length= -0x000000082, T = int]':
a.cpp:77:9:   instantiated from 'void test(T*) [with int length= -0x000000081, T = int]'
a.cpp:77:9:   instantiated from 'void test(T*) [with int length= -0x000000080, T = int]'

Last two lines, is pretty much the same as the first ones.

To me it would seem, its not catching the specialization, hence:

a.cpp:83:43: error: template-id 'test<0>' in declaration of primary template

Am I correct?

And if I'm correct, I'm guessing it's the issue that partial template specialisation is not allowed for function templates, so what would be the solution then, making a struct, and using specialisation on that?

Skeen
  • 4,614
  • 5
  • 41
  • 67

4 Answers4

23

Partial specialization of function templates is not allowed. Herb Sutter explains why in his article "Why Not Specialize Function Templates?".

To work around this limitation you need to use class templates instead. You can then write a regular function template that uses that class template.

That specific error you're getting is because you forgot the second parameter in your specialization. If you this instead:

template<int length, typename T>
void test(T* array)
{
    //...
    test<length-1,T>(array);
}


template<typename T>
void test<0,T>(T* array)
{
    return;
}

GCC complains with the following:

error: function template partial specialization 'test<0, T>' is not allowed

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • So what would be my solution then, using a struct, and creating a wrapper function? – Skeen Jul 08 '11 at 10:49
  • 2
    And why isn't it allowed? - just curious. – Skeen Jul 08 '11 at 10:49
  • 1
    @Skeen To quote the article, "you can't partially specialize them -- pretty much just because the language says you can't". With a footnote that it's being considered as a language feature in a future version of C++ (which would have been C++11 at the time). – Chuu Jan 29 '19 at 23:53
  • Thank you for the answer. I have a question from the link provided. In Example 2, how does the compiler decide whether (c) is a specialization of (a) or (b)? – CodePro_NotYet Jun 12 '21 at 04:52
9

Functions cannot be partially specialized. To work around this, have your template function call a function in a class:

template<int length, typename T>
struct helper
{
    static void go(T* array)
    {
        ...
        helper<length-1, T>::go(array);
    }
};

template<typename T>
struct helper<0, T>
{
    static void go(T* array)
    {
        ...
    }
};

template<int length, typename T>
void test(T* array)
{
    ...
    helper<length, T>::go(array);
}
Dark Falcon
  • 43,592
  • 5
  • 83
  • 98
3

Partial specialization of function templates is not allowed.

To work around that, you could make test a static member of a class template, and partially specialize the class.

Community
  • 1
  • 1
NPE
  • 486,780
  • 108
  • 951
  • 1,012
2

You can workaround with a helper class. For illustrational purposes:

template<typename T, typename U> struct helper_t {
    static int foo () { return 0; }
};

template<typename T> struct helper_t<T,T> {
    static int foo () { return 1; }
};

template <typename T, typename U>
int frob () {
    return helper_t<T,U>::foo();
}
Sebastian Mach
  • 38,570
  • 8
  • 95
  • 130