9
votes

I'm learning about templates in c++ and I found the following example.

From what I understand, the compiler should always try to use the most "specialized" template if there is no non-template functions matching, yet in this example, the first call result in calling function a(T*) instead of a(int*). Why? And why does second call act differently?

template<typename T>
void a(T) {cout << "(T)" << endl;}

template<>
void a<>(int*) {cout << "(int)" << endl;}

template<typename T>
void a(T*) {cout << "(T*)" << endl;}

template<typename T>
void b(T) {cout << "(T)" << endl;}

template<typename T>
void b(T*) {cout << "(T*)" << endl;}

template<>
void b<>(int*) {cout << "(int)" << endl;}

int main()
{
  int i;
  a(&i);
  b(&i); 
  return 0;
}

The resulting output is:

(T*)
(int)

I expected it to be:

(int)
(int)
2

2 Answers

6
votes

Only primary templates (so no specializations) are taken into account to select more specialized overloads.

Once selection is done with primary template, we use the specialization if any.

Now, template<> void a<>(int*); can only be specialization of template<typename T> void a(T) (the other version has not been seen).

and template<> void b<>(int*); is specialization of template<typename T> void b(T*) (it is the more specialized matching overloads).

Notice that you might select specialization of b by providing template instead of letting compiler deduce:

  • template<> void b<>(int*) -> template<typename T> void b(T*) with T=int
  • template<> void b<int>(int*) -> template<typename T> void b(T*) with T=int
  • template<> void b<int*>(int*) -> template<typename T> void b(T) with T=int*

so for:

int i;
a(&i); // a<T*> with T=int*, no specialization for a<U*> (U=int) exist -> generic template called
b(&i); // b<T*> with T=int*, specialization for b<U*> (U=int) exists -> specialization called
2
votes

Due to the difference of the declaration order, the specialization belongs to different primary template.

For the 1st case, the specialization belongs to a(T). For the 2nd case, the specialization belongs to b(T*). During overload resolution between primary templates, the version taking T* is always selected; then in the 1st case, the specialization won't be considered because it's not the specialization of a(T*). But it's selected in the 2nd case because b(T*) has the specialization.

i.e.

For the 1st case

overloading

  • a(T)
    • specialization --> a(int*)
  • a(T*) // selected in overload resolution

For the 2nd case

overloading

  • b(T)
  • b(T*) // selected in overload resolution
    • specialization --> b(int*) // then the specialization is selected