按照我们之前的习惯,写出的代码是这样的
// Template.h #pragma once #include <stdio.h> #include <iostream> using namespace std; int Add(int a,int b);//对普通函数的声明 template<class T> T Sub(T a,T b);//对模板函数的声明 //Template.cpp #include "Template.h" int Add(int a,int b)//普通函数的定义 { return a+b; } template<class T> T Sub(T a,T b)//模板函数的定义 { return a-b; } //test.cpp #include "Template.h" int main() { cout<<"ret_1:"<<Add(1,2)<<endl; cout<<"ret_2:"<<Sub(3,6)<<endl; return 0; }
Makefile文件是这样写的:
a.out:Template.cpp test.cpp g++ -g -o $@ $^ .PHONY:clean clean: rm -f a.out
我们进行编译后发现:
说未定义的Sub函数,我们这里的Add函数其实是和Sub函数一样定义与声明分离的,Add函数并没有报错
解释:
当编译器对test.cpp文件进行编译时,再展开的头文件中只找到了Add()函数和Sub()函数的声明,并没有找到其定义这里的call指令后面的地址是缺省的(可以先这样理解),在链接这一步骤时,Add函数的定义会在template文件中找到,其实着函数其实是用函数名在符号表中去找对应的函数名和对应的地址,根据上面的编译结果来看,Add函数的定义是找到了,那么为什么Sub函数没有找到呢?我们之前讲过,函数模板只有在实例化后才会生成相应的代码,从这一点来看,template.cpp文件中只是有模板函数的定义,并没有实例化模板参数,所以编译时并没有生成代码,Sub函数也不会出现在符号表中,在链接找不到Sub函数的定义,就会出现链接错误。 |
模板是不支持分离编译的
// Template.h #pragma once #include <stdio.h> #include <iostream> using namespace std; int Add(int a,int b);//对普通函数的声明 template<class T>T Sub(T a,T b);//对模板函数的声明 //Template.cpp #include "Template.h" int Add(int a,int b)//普通函数的定义 { return a+b; } template<class T> T Sub(T a,T b)//模板函数的定义 { return a-b; } //模板参数实例化templateint Sub<int>(int,int);//test.cpp #include "Template.h" int main() { cout<<"ret_1:"<<Add(1,2)<<endl; cout<<"ret_2:"<<Sub(3,6)<<endl; return 0; }
这样在template.cpp文件中就会生成Sub函数的代码
但是如果有不同的实例类型,那就需要一个一个进行参数实例化,所以说这种方法并不是很好
这样template.cpp文件就会在test.cpp文件中进行展开,其实这样的话,我们根本不需要在头文件中对函数进行声明了,但是这种方法如果你在别的项目中要用的话,就要除了包含template.h文件还要包含其函数的定义文件template.cpp文件.
最好是写在一个.h(或者是.hpp文件)文件中,上面放函数的声明(方便查找),下面放函数的定义
文章来源: 为什吗C++中的模板不支持分离编译