Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

function template specialization failed?

#include <iostream>

template <class T> 
void foo(T) {
    std::cout << "foo(T)" << std::endl;
}

template <class T> 
void foo(T*) { //#3
    std::cout << "foo(T*)" << std::endl;
}

#define TEST

#ifdef TEST
template <> 
void foo(int*) { //#1
    std::cout << "foo(int*)" << std::endl;
}
#else
template <>
void foo<int*>(int*) { //#2
    std::cout << "foo<int*>(int*)" << std::endl;
}
#endif

int main(int argc, char **argv) {
    int* p = 0;
    foo(p);
    return 0;
}

What is the difference between #1 and #2 above? If I define TEST, then #1 works, but if I comment it out, #3 works. Which is the right way to write function template specialization here?

like image 511
Xiaotian Pei Avatar asked Jan 21 '12 11:01

Xiaotian Pei


2 Answers

#1 declares a function template specialization of #3 and automatically deduces the template parameters. #2 is a specialization of the first template you defined (the one without number, let's call it #0) for T=int*. It can't be a specialization of #3 because replacing T with the specified int* there would lead to a int** parameter.

When you call foo, overload resolution now first picks the best fitting base template, then checks that template for any existing specializations. With TEST defined, there are two base templates (#0 and #3) and #3 is a better match and gets selected. Then the compiler checks for specializations of that template, and #1 is a better fit and is being called.

Without TEST defined, there are still two base templates (#0 and #3) and #3 is a better match and gets selected. Then the compiler checks for specializations of that template, but since #2 specializes #0 and not #3, it is not considered and #3 ends of being called.

This is the classical example of Why not Specialize Function Templates. The problems are explained in more detail there.

The simple solution is to not specialize function templates at all, but simply add new overloads for the special types:

// no template, just a normal function
void foo(int*) {
    std::cout << "foo(int*)" << std::endl;
}
like image 134
sth Avatar answered Oct 03 '22 03:10

sth


For function template specializations you can explicitly list the template arguments but you don't have to if the template arguments are deduced. If you don't specify the template arguments, they are deduced by the compiler using the same rules as overload resolution. For deciding which function overload is to be chosen, the compiler starts with looking only at the primary templates (which are selected by some magical process in the first place). Looking at the two available primary templates

template <typename T> void foo(T);
template <typename T> void foo(T*);

the latter is a better match for a pointer argument. Once the proper primary template is found, the compiler looks for potential specializations of this primary template. However, your example #2 actually is not a specialization of function template taking a pointer argument although it involves a pointer argument. If you take the primary declaration

template <typename T> void foo(T*);

and you replace T by the explicitly specified template argument int* you get

template <> void foo<int*>(int**);

That is, the declaration

template <> void foo<int*>(int*);

is something different. You probably just want to lose the pointer when specifying the template argument:

template <> void foo<int>(int*);
like image 36
Dietmar Kühl Avatar answered Oct 03 '22 01:10

Dietmar Kühl