1.2.5 模板函数的静态变量
我们还是恢复到最通用的旧标准上。前面说过,编译器会根据函数模板自动生成函数实例。通过实际考查函数模板中的静态成员变量的实现方法,可以对这一机制进行侧面印证。例1.5中的func函数模板中有两个静态变量sv0和sv2。普通函数中的静态变量可以解释为函数自有,不同函数之间的同名静态变量不通用。那么,函数模板中的静态变量如何实现呢?在回答这个问题前,先看一下例1.5编译运行的输出,如下所示:
- v1: 1 v3: 2 v4: 3 || sv0: 0 sv2: 0
- v1: 1 v3: 2 v4: 3 || sv0: -1 sv2: -1
- v1: 1 v3: 0.1 v4: 0.1 || sv0: 0 sv2: 0
- v1: 0.1 v3: 0.1 v4: 0.1 || sv0: 0 sv2: 0
在func函数模板中,静态变量sv0和sv2每调用一次就自减1。所以如果调用的是同一函数的话,sv0和sv2的值会有变化。再看以上程序的输出,只有第二行的sv0与sv2的值有变化,其他三行都是初值0。这说明第二次调用func生成的函数与第一次(也只可能与第一次)调用的函数应该是同一函数,共用相同的静态函数变量;而第三、第四次调用的则是另外的函数。在四次调用中,总共调用了三个不同的函数,才会出现三组0值。再仔细检查四次调用时的模板参数值就会发现,第一次和第二次调用时模板各参数值都相同,为T0=double,而T1~T4都等于int。而第三、第四次调用时模板参数值均与之前不同。这说明,当有多个调用使用相同的模板参数值时,编译器只为此模板参数值组生成同一函数而将不同调用都链接到同一函数上。只要有任意模板参数值不同,编译器就会生成不同的函数实体以供链接。
编译器是否真如我们所说,为相同参数值生成唯一函数实体呢?编译生成的目标文件可做另一佐证。当然,方法依编译器及编译平台而不同。还是以笔者所用Linux系统中的GCC 4.6.1编译器为例,将例1.5的程序编译成可执行目标文件(最简单的方法是使用默认输出文件名a.out),使用命令nm查看其中符号表时可以发现有如下三个符号:
- $nm -C a.out | grep func | grep W
- 0000000000400abc W int func<double, int, int, double, double>
- (int, double, double)
- 00000000004009fb W int func<double, int, int, int, int>
- (int, int, int)
- 0000000000400b81 W double func<int, double, double, double, double>
- (double, double, double)
- $
输出清晰地显示目标文件中只有三个func函数模板实例。根据其模板参数类型可以确定,编译器为同一组模板参数值只生成唯一函数实体。