在 C++ 中为了操作简洁引入了函数模板。所谓的函数模板实际上是建立一个通用函数,其函数类型或形参类型不具体指定,用一个虚拟的类型来表达,这个通用函数就称为函数模板。
1、通用的写法
函数模板不是一个具体的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对函数功能框架的描述,当它具体执行时,将根据传递的实际参数决定其功能。为了容易使用,一般通用的写法都是在头文件中直接定义函数模板,定义的同时也是声明该函数,供给其它文件包含调用。
1 //------fun.h或fun.hpp------// 2 #ifndef _FUN_H_ 3 #define _FUN_H_ 4 5 using namespace std; 6 7 template<typename T> 8 void fun(int b, T c, T d) //定义函数模板 9 { 10 ...... 11 } 12 13 #endif
对编译器而言,定义函数模板的时候,编译器并不会对它进行编译,因为它没有一个实体可用,编译器只看到了声明,只有模板被实例化后(用在特定的类型上),编译器才会根据具体的类型对模板进行编译。因此当在别的文件中调用该函数模板时,根据传递的实际参数决定其功能,这样编译器就可以在编译期间看到模板函数的定义并完成模板的实例化,如果在编译的时候,找不到模板函数的定义,就先不在这一次编译中实例化该模板函数。
2、问题的引出
但是头文件中定义和使用函数模板时,碰到了一个这样的场景,即在函数模板中使用到了全局变量:
1 //------fun.h或fun.hpp------// 2 #ifndef _FUN_H_ 3 #define _FUN_H_ 4 5 using namespace std; 6 int a; //定义全局变量 7 template<typename T> 8 void fun(int b, T c, T d) //定义函数模板 9 { 10 ...... 11 a = b; 12 } 13 14 #endif
因此碰到其它多个文件需要使用该函数模板时,都需要各自包含该函数模板的头文件,编译时就会出现“全局变量重复定义”的错误。
尝试按照普通函数定义和声明分开的思路将函数模板的定义和声明分开:
源文件:
1 //------fun.cpp------// //错误做法 2 using namespace std; 3 int a; //定义全局变量 4 template<typename T> 5 void fun(int b, T c, T d) //定义函数模板 6 { 7 ...... 8 a = b; 9 }
头文件:
1 //------fun.h或fun.hpp------// //错误做法 2 #ifndef _FUN_H_ 3 #define _FUN_H_ 4 5 extern a; 6 template<typename T> void fun(int b, T c, T d); 7 8 #endif
经过尝试,按照普通函数的方式将函数模板的定义和声明分开,在其它文件中调用函数模板,编译时就会出现“找不到该函数定义”的错误。
那么有没有办法将函数模板的定义和声明正确分开,提供给其它文件包含调用呢,答案肯定是有的。
3、问题的解决
针对上述第2点所阐述的函数模板使用的这一场景,需要将函数模板的定义和声明分离开来,根据实际的应用,使用以下的做法可以很好的解决这一问题,编译和调用都没有问题。
首先是源文件*.cpp的实现:
1 //------fun.cpp------// 2 using namespace std; 3 int a; //定义全局变量 4 template<typename T> 5 void fun(int b, T c, T d) //定义函数模板 6 { 7 ...... 8 a = b; 9 } 10 11 template void fun(int b, int c, int d); //函数模板实例化,此时T被int替代 12 template void fun(int b, char c, char d); //函数模板实例化,此时T被char替代
因此在源文件中操作有:
(1)、定义需要使用的函数模板;
(2)、在定义的函数模板后进行函数实例化操作,通过这样的方法实现具体的模板函数。
接着是头文件*.h或者*.hpp的实现:
1 //------fun.h或fun.hpp------// 2 #ifndef _FUN_H_ 3 #define _FUN_H_ 4 5 extern a; 6 template<typename T> void fun(int b, T c, T d); 7 extern template void fun(int b, int c, int d); 8 extern template void fun(int b, char c, char d); 9 10 #endif
因此在头文件中需要的操作有:
(1)、声明定义的函数模板;
(2)、使用extern的方式声明实例化后的模板函数。
总结
可见,将函数模板的定义和声明分开,需要额外在源文件中进行函数模板的实例化再在头文件中进行声明,多了一些步骤。在无特定的使用的场景中,还是建议将函数模板放在头文件中直接定义并调用;当然,如果碰到一些跨文件调用的特定场景,那么采用这种将函数模板的定义和声明分开的方法也是OK的。
更多技术内容和书籍资料获取敬请关注微信公众号“明解嵌入式”