17

Could someone explain why once is used method c(T*) and next time d<>(int*) ? methods c and d seems identical to me and I cannot figure out why is not the same type of method called.

#include <iostream>
using namespace std;

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

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

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

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

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

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

int main(){
    int i;
    c(&i);
    d(&i);
    return 0;
}

Output:

(T*)
(int*)
Jarod42
  • 203,559
  • 14
  • 181
  • 302
zlenyk
  • 922
  • 2
  • 7
  • 22

3 Answers3

18

You just stumbled on an ugly part of C++.

The overload resolution pass, during compilation, is about finding the best overload for the current code. It is executed on a set of function and function templates that was selected by the look-up phase, and aims at identifying one (and only one) overload that is better than others.

For function templates, they are segregated in two groups:

  • "base" function templates
  • specialized function templates

and the overload resolution process has two steps:

  1. Pick the best match among the regular function and "base" function templates
  2. If a "base" function template is selected by step 1., pick the best specialization (if any matches, otherwise use the "base")

In both of your examples, the best "base" function is c(T*) and d(T*), so this is the second step that differ. Why ?

Because, to be a specialization of a function template, said function template has to be declared first.

Thus:

  • c<>(int*) is a specialization of c(T)
  • d<>(int*) is a specialization of d(T*)

and therefore, when c(T*) is picked in step 1., then there is no better specialization whilst when d(T*) is picked, d<>(int*) is a better specialization.

Because this is tricky, the recommendation by experts... is NOT to use function template specialization. It just mixes weirdly with function template overload.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
15
template <typename T>
void c(T);      // 1: function template

template <>
void c<>(int*); // 2: specialization of 1

template<typename T>
void c(T*);     // 3: overload of 1

template<typename T>
void d(T);      // 4: function template

template<typename T>
void d(T*);     // 5: overload of 4

template<>
void d<>(int*); // 6: specialization of 5

// ...

int i;

c(&i);          // 3 is more appropriate overload than 1

d(&i);          // 5 is more appropriate overload than 4
                // and it has the suitable specialization 6

Diagram:

                    c        d
                   / \      / \
overloads         1  (3)   4  (5)   |
                  |            |    |  priority
specializations   2           (6)   V
Constructor
  • 7,273
  • 2
  • 24
  • 66
2

Overload resolution only selects a base template (or a nontemplate function, if one is available). Only after it's been decided which base template is going to be selected, and that choice is locked in, will the compiler look around to see if there happens to be a suitable specialization of that template available, and if so that specialization will get used.

For function c the specialization void c<>(int*) is for void c(T) overload while for d the specialization void d<>(int*) is for void d(T*) overload.

So by the above explanation, first void c(T) is inspected and is ignored over the better overload void c(T*) (which has no specializations) which gets printed. For d too the void d(T*) overload is locked in but then the specialization void d(int*) is noted and is then chosen.

legends2k
  • 31,634
  • 25
  • 118
  • 222