函数模板中的魔术参数

Pra*_*rav 21 c++ templates

在以下代码中

#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);
 }
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,两个阵列的大小都被打印出来.但是N如何自动初始化为数组大小的正确值(数组是通过引用传递的)?上面的代码是如何工作的?

AnT*_*AnT 32

N没有"初始化"任何东西.它不是变量.它不是一个对象.N是一个编译时常量.N仅在编译期间存在.值和N实际T值由称为模板参数推导的过程确定.双方TN从实际类型传递到模板函数的参数推导.

在第一次调用时,参数类型是int[6],所以编译器推导出它,T == intN == 6为此生成一个单独的函数并调用它.我们来命名吧cal_size_int_6

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

请注意,此功能中没有T也没有N.在编译时,两者都被它们的实际推导值所取代.

在第一次调用中,参数类型是int[1],所以编译器推导出它,T == intN == 1为它生成一个单独的函数并调用它.我们来命名吧cal_size_int_1

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

这里也是一样的.

main基本上翻译成

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

  cal_size_int_6(a); 
  cal_size_int_1(b); 
} 
Run Code Online (Sandbox Code Playgroud)

换句话说,你的cal_size模板生下了两个不同的功能(所谓特化的原始模板),每一个都具有不同的值N(和T硬编码到体).这就是模板在C++中的工作方式.

  • 实际上,comeau接受这个:`template <unsigned char S> void f(int(&)[S]); int main(){int a [UCHAR_MAX + 1]; F A); }`.有趣的是,我甚至不确定标准中关于这个片段的内容(似乎说调用成功,但没有指定`S`在函数体中有什么值).在任何情况下,comeau都给`S`类型为"unsigned char",但在函数体内给出它的实际值(在其范围之外). (3认同)

Ada*_*eld 8

它的作用是因为类型a是"长度为6的数组int",类型b是"长度为1的数组int".编译器知道这一点,因此它可以调用正确的函数.特别是,第一个调用调用模板实例cal_size<6>()和第二个调用调用cal_size<1>(),因为这些是唯一匹配其各自参数的模板实例.

如果您尝试调用显式模板实例,则仅在大小合适时才会起作用,否则参数将不匹配.考虑以下:

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"
Run Code Online (Sandbox Code Playgroud)