模板函数的声明和定义为何不能分开放在两个文件中?

对于普通函数来说,声明放在头文件,定义放在源文件,其它地方要使用该函数时,仅需要包含头文件即可。因为编译器编译时是以一个源文件作为单元编译的,当它遇到不在本文件中定义的函数时,若能够找到其声明,则会将此符号放在本编译单元的外部符号表中,链接的时候自然就可以找到该符号的定义。

而对于模板函数来说(在编译器遇到使用模板函数的代码时才会将模板函数实例化),若将模板函数声明放在tem.h,模板定义放在tem.cpp,然后在main.cpp中包含该头文件,调用add函数,此时仅有声明,找不到定义,因此编译器只能实例化函数的外部符号,但不会实例化int add(int,int)函数的具体实现。对于普通函数来说,此时的add函数已经由编译器生成对应的代码,而对于模板函数来说,此时并没有生成add函数对应的代码,但是编译main.cpp时不会报错,而在链接时就会出现add函数未定义的错误。

可以通过显式定义来实例化模板函数,即使用语句temmplate int add(int,int)。 编译器根据该语句会生成add方法的int版本,此时链接就不会报错。此外,这样做通常也能够提高编译的效率。试想,如果在tem.h文件内定义模板,假如有三个源文件均包含了该头文件且均使用了模板(假定均调用了add模板的int版本),则在这三个源文件内必然都会生成add函数的实例。显然效率不高。而如果像上面那样显示定义模板,则只会在tem.cpp文件中实例化。

最后,对于模板类来说,也同样符合上面的原则。我们知道类的定义只是声明了类的成员函数,实际上编译器会把类的成员函数编译成修改名称后的全局函数。在使用类模板的时候,首先会实例化类,同时实例化类相应的构造函数。在使用类模板的实例调用相应的模板成员函数时,才会实例化该函数。如果类的模板成员函数定义与类的定义不在同一个编译单元中(分离式编译),此时调用成员函数便会出现未定义的错误。而显式的调用它时就不会出现问题。

总结:编译器只要遇到使用模板函数的地方就会实例化相应的函数,若在此编译单元内没有模板函数的定义,它就不能够实例化成功。因此通常情况下模板函数的声明与定义都放在同一文件内,这样就能保证在使用模板的地方一定可以实例化成功。同时,由编译器保证只生成某种类型的一个实例化版本,不用担心重复实例化的问题。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
#include <iostream> using namespace std; //在外进行前置声明 // 友元函数外面实现,并且都在同一个CPP文件, //需要在前面对和对应操作符重载函数进行前置声明。 // 同时在内部声明的对应函数需要在参数链表前加入 "<T>"。 template <class T1, class T2> class Person; template <class T1, class T2> std::ostream& operator<<(std::ostream& os, Person<T1, T2>& p); //模板 外实现 //模板 template <class T1, class T2> class Person { public: //模板使用友元 //重载左移操作符,用于直接打印输出 //方法1 //template <class T1, class T2> //定要加 这句,不过这样的写法在linux不能通过 //friend std::ostream& operator<<(std::ostream& os, Person<T1, T2>& p); //方法2在操作符后加 <T1,T2>,同时进行前置声明 //方法1 在VS可以便宜,但 要在Linux下会出错 friend std::ostream& operator<<<T1,T2>(std::ostream& os, Person<T1, T2>& p); Person(T1 id, T2 age); void showPerson(); private: T1 mId_; T2 mAge_; }; //声明和实现分开时,我们需要对函数声明做特殊处理, //注意,这里名应是Person<T1,T2> template <class T1, class T2> Person<T1, T2>::Person(T1 id, T2 age) { this->mAge_ = age; this->mId_ = id; } template <class T1, class T2> void Person<T1, T2>::showPerson() { std::cout << "id.....:" << mId_ << ",age:" << mAge_ << std::endl; } //通过重载左移操作符,用于直接打印输出 template <class T1, class T2> std::ostream& operator<<(std::ostream& os, Person<T1, T2>& p) { std::cout << "operator--- id: " << p.mId_ << ",age:" << p.mAge_ << std::endl; return os; } //模板外实现时,不要滥用友元 void testShow() { //函数模板在调用时,可以自动进行型推导 //模板必须显式声明 Person<int, int> person(12, 11111); //person.showPerson(); std::cout << "ddd: " << person << std::endl; } int main() { testShow(); return 0; }
06-11
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值