Question

#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. If I define TEST, #1 work. But if I comment it, #3 work... And which is the right way to write function template specialization...

Was it helpful?

Solution

#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;
}

OTHER TIPS

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*);

I can't really tell which function #2 is supposed to specialize, or exactly how the extremely complicated overload resolution rules would select the function to call.

I do know that you most often don't need to specialize functions, but can rely on overloading instead. To get a function for int* you just need

void foo(int*) {
    std::cout << "foo(int*)" << std::endl;
}

A non-template function will be preferred over templates, as long as the parameter matches.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top