1.问题,最近看项目log模块,_log模板函数中的一个参数竟然看蒙x了。函数原形是这样:
template<size_t size>
void _Log(char (&strDest)[size], const char *scetion, const char *key, const char *msg, va_list &parm);
对!就是 char (&strDest)[size]没有看懂。这个传进来的是个字符串数组。
2.翻开c++圣经<c++primer>,找到答案。设计模板类型形参中的 非类型形参和数组引用类型形参。
首先,数组作为形参,有引用和非引用两种形式。一般我们使用非引用形式,也就是将形参定义为数组元素类型的指针,一共有三个等价形式:
1.void func(int *p);
2.void func(int p[]);
3.void func(int p[10]);
其中第三个显示的表明 调用该函数时,所传递的数组最多有10个元素。但是实际上编译器会忽略为任何数组指定的形参长度,换句话说,第三个函数声明中的10,其实是被编译器忽略的。
当我们调用上边的函数时,传递数组名时,数组名会自动转化为指向数组第一个元素的指针。也就是该指针所存放的是数组第一个元素的地址。 然后形参复制的是这个指针指向的地址。
然后,当数组作为形参,采用引用的方式时,形式是这样:
1.void func(int (&p)[10]);
当调用此函数时,必须传递的是 具有10个int型元素的数组。类似这样
int a[10] = {};
func(a);
如果这样 int b[2] = {}; func(b); 是不正确的,因为b不是10个元素。当形参是引用类型时,理解引用的含义很重要。引用就是一个变量的别名,但是终究是该变量自己。
同时,使用引用时,没有复制的环节,大多时候会节省空间,提升效率。
3.但是,我们可以看出,以上形式的数组的引用型形参,是很不方便的。因为传递数组时,存在类型和长度的双重限制。
那么有没有一种方法,能够解决上述问题呢? 既能用到引用的有点,同时又能避免长度带来的限制?
答案就是采用 1. 中的模板函数以及使用非类型模板形参。
template<size_t size>
void func(int (&p)[size]){ ... }
圣经中讲到:模板非类型形参是模板定义内部的常量值,在需要常量表达式的时候,可使用非类型形参指定数组的长度, 当调用 func是,编译器会从数组的实参计算非类型形参的值,也就是编译器替我们
计算好了size的值,从而省去我们自己传递长度。这样一来,就可以传递数组元素相同的数组了,不必担心长度。从而避免了2中的缺点
这样一来:
int a[10], b[2];
func(a);func(b)都可以正确调用,同时size的值为a和b的数组长度。
另外一点需要说明,尽管用了模板的非类型形参,让我们可以省去数组长度的限制,但是实际上,编译器会产生两个func的实例 func<10>, func<2>.
如果此时声明 int c[10]; func(c); 此时a和c将使用相同的实例。
这基于: 对于模板的非类型形参而言,求值结果形同的表达式将认为是等价的。
因此 a和c调用 func(int (&p)[10]), b调用 func(int (&p)[2]).
4.至此,_log函数中的第一个形参已经弄清--数组的应用型形参。同时利用模板的非类型形参,除去了数组长度的限制。
水平有限,欢迎指正错误。