tags:

views:

176

answers:

3

In the following code

#include<iostream>

 template<typename T,size_t N> 
 void cal_size(T (&a)[N])
 { 
     std::cout<<"size of array is: "<<N<<std::endl;
 }

 int main()
 {
     int a[]={1,2,3,4,5,6};
     int b[]={1};

     cal_size(a);
     cal_size(b);
 }

As expected the size of both the arrays gets printed. But how does N automatically gets initialized to the correct value of the array-size (arrays are being passed by reference)? How is the above code working?

+1  A: 

when you declare int a[] = {1,2,3} it is the same as (or will be rewritten as) int a[3] = {1,2,3} since the templated function is receiving argument in form of T a[N], then N will have value of 3.

uray
+5  A: 

It works because the type of a is "array of length 6 of int" and the type of b is "array of length 1 of int". The compiler knows this, so it can call the correct function. In particular, the first call calls the template instance cal_size<6>() and the second call calls cal_size<1>(), since those are the only template instantiations which match their respective arguments.

If you attempted to call an explicit template instance, it would only work if you got the size right, otherwise the arguments wouldn't match. Consider the following:

cal_size(a);    // ok, compiler figures out implicitly that N=6
cal_size<int, 6>(a); // also ok, same result as above
cal_size<int, 5>(a); // ERROR: a is not of type "array of length 5 of int"
Adam Rosenfield
+12  A: 

N does not get "initialized" to anything. It is not a variable. It is not an object. N is a compile-time constant. N only exists during compilation. The value of N as well as the actual T is determined by the process called template argument deduction. Both T and N are deduced from the actual type of the argument you pass to your template function.

In the first call the argument type is int[6], so the compiler deduces that T == int and N == 6, generates a separate function for that and calls it. Let's name it cal_size_int_6

void cal_size_int_6(int (&a)[6]) 
{ 
  std::cout << "size of array is: " << 6 << std::endl; 
} 

Note that there's no T and no N in thsi function anymore. Both were replaced by their actual deduced values at compile time.

In the first call the argument type is int[1], so the compiler deduces that T == int andf N == 1, generates a separate function for that as well and calls it. Let's name it cal_size_int_1

void cal_size_int_1(int (&a)[1]) 
{ 
  std::cout << "size of array is: " << 1 << std::endl; 
} 

Same thing here.

Your main essentially translates into

int main() 
{ 
  int a[]={1,2,3,4,5,6}; 
  int b[]={1}; 

  cal_size_int_6(a); 
  cal_size_int_1(b); 
} 

In other words, your cal_size template gives birth to two different functions (so called instantiations), each with different values of N (and T) hardcoded into the body. That's how templates work in C++.

AndreyT
In fact, comeau accepts this: `template<unsigned char S> void f(int( int main() { int a[UCHAR_MAX+1]; f(a); }`. The funny thing is, i'm not even sure what the Standard says about this snippet (it seems to say the call succeeds, but does not specify what value `S` has in the function body). In any case, comeau gives `S` the type "unsigned char", but gives it the real value (outside of its range) within the function body.
Johannes Schaub - litb