Search code examples
c++c++11templatesraw-types

What is the sense behind Template Type?


By using templates:

template <class T>
T GetMax (T a, T b) {
  return (a>b?a:b);
}

and then

int main () {
  int i=5, j=6, k;
  long l=10, m=5, n;
  k=GetMax(i,j);           // line 1
  n=GetMax<int>(l,m);      // line 2
  n=GetMax<double>(l,m);   // line 3
  cout << k << endl; 
  cout << n << endl;
  return 0;
}

my questions are:

why do I need to do this:

n=GetMax<int>(l,m);      // line2

if I can do

n=GetMax(l,m);      // line 2

and why this compiles?

n=GetMax<double>(l,m);     

when l and m are integers which are far away different from a double?


Solution

  • In this context you can think that template instantation is a simple text replacement (It's not that far from it), that is once you have the template parameters figured out. So:

    1. You let the compiler figure out the type from the operands. Since they match, T is figured out to be int. You can find the exact logic of ranking matching template parameters in the standard. Your case is pretty straightforward.

    After figuring out template parameter, the substitution is made, so your function looks like:

    int GetMax (int a, int b) {
       return (a>b?a:b);
    }
    

    It does get compilcated, when you use multiple parameters and have many possible matches with inheritance etc, etc.

    For your case you can provoke equal matches, by using two different types for params i.e: GetMax(i,l). T has two candidates: double as int, and both are equally good.

    This is related to the famous SFINAE (substitution failure is not an error). Compiler tries to generate versions for possible parameter combinations, and if it fails, it's not taken for consideration for final ranking. In your case two substitutions were successful.

    1. You explicitly state that T is int. So the compiler does not invoke automatic parameter matching. The instantiated version looks the same as 1.

    2. You explicitly state that T in double. Again, the compiler does not invoke any logic regarding the types.

    It blindly generates version for double, and uses it:

     double GetMax (double a, double b) {
        return (a>b?a:b);
     }
    

    It's legal to pass an int to a function using double, an implicit conversion happens.

    This is perfectly legal:

    void f(double d) {};
    
    int main(){
        int i = 5;
        f(i);
    }
    

    Bottom line:

    Figuring out template types is a different piece of logic than invoking the function. Think of it as separate phases. Type deduction is designed to make sense for the invocation, but it's a separate thing.