设为首页 加入收藏

TOP

1.3.1 HPP文件还是CPP文件
2013-10-07 16:31:01 来源: 作者: 【 】 浏览:192
Tags:1.3.1 HPP 文件 还是 CPP

1.3 如何处理函数模板中的函数体

既然编译器是在需要生成模板实例时自动生成,这就带来一个与传统C/C++(www.cppentry.com)编程(www.cppentry.com)习惯的冲突,即函数模板中的函数体应该放在哪里。

1.3.1 HPP文件还是CPP文件

按照C++(www.cppentry.com)语言习惯,普通函数及类的声明应该放在一个头文件(通常以h、hpp或者hh为扩展名)里,而将其实现放在一个主代码文件(通常以c、cpp或者cc为扩展名)里,这样便于将代码分散编译到多个目标文件中,最后通过链接形成一个完整的目标文件。但是由于模板的实现是随用随生成,并不存在真实的函数实现代码,如果还是按照“头文件放声明,主文件放实现”的做法,则会导致编译失败。

例如一个最简单的模板函数声明如下所示:

  1. // 文件名func.hpp  
  2. template<typename T> 
  3. T const& func(T const &v); 

这个声明放在一个名为“func.hpp”的头文件中,其实现放在名为“func.cpp”的文件中,代码如下所示:

  1. // 文件名func.cpp  
  2. template<typename T> 
  3. T const& func(T const &v) {return v;} 

在一个名为“main.hpp”的文件中定义一个main函数用来调用func函数模板,代码如下所示:

  1. // 文件名main.cpp  
  2. #include "func.hpp"  
  3. int main() {func(0);} 

正如我们通常安排普通函数的代码那样,如果单独编译这两个CPP文件都没有问题,但是在链接两个目标文件时链接器就会报错。在笔者的编译环境下出现的错误如下:

  1. $ g++ func.o main.o  
  2. main.o: In function 'main':  
  3. main.cpp:(.text+0x17): undefined reference to `int const& func<int>(int const&)'  
  4. collect2: ld 返回 1 

链接器报的错误是func<int>即func函数模板的某个实例未定义。按常理,这样一个函数的实现应该是在func.cpp编译出的目标文件中定义。但如果查看该目标文件(笔者用的是func.o)就会发现其中空空如也,并无任何函数定义。

回想一下模板的工作原理就不难理解这一现象。编译器在编译func.cpp时,只是读到了func函数模板的实现,并没有读到任何需要生成函数模板实例的语句,所以不会生成任何func函数的实例。而在编译main.cpp时,虽然用到了一个函数模板实例,但因为main.cpp只是将func.hpp头文件包含进来,而后者只有一个func函数模板的声明,并无具体函数体实现,此时编译器也无法生成func函数模板实例,只好预留一个调用链接,期望在最后的链接过程中可以找到函数实现。但很遗憾这样的实现并不存在,于是最后链接时出错。

稍微修改func.cpp中的代码,使其生成一个func<int>的函数实现,如例1.6所示。

例1.6

  1. // 文件名func2.cpp  
  2. template<typename T> 
  3. T const& func(T const &v) {return v;}  
  4.  
  5. template int const& func(int const &v); 

例1.6中用到一种尚未介绍过的语法——明确生成模板实例。当关键字template后没有模板参数列表,而是一个函数声明时,意味着指示编译器根据此函数声明寻找合适的模板实现。当然,所声明函数必须与某一已知模板函数同名,并且其参数可用模板匹配。

例1.6中将函数声明为T=int,从而在编译func2.cpp时,会在目标文件中生成func<int>的代码而不会在链接时产生错误。但这只是权宜之计,倘若还需要func<float>或者func<char>,那么在代码文件中还得增加相应的语句,以促使编译器生成相应函数模板实例。如此一来,又变成由人工生成模板实例,违背了当初由编译器随用随生成的初衷。

可见,虽然模板中的函数也可以有自己的声明和实现,但编译器不会在读到模板实现时立刻生成实际代码,因为具体的模板参数类型还未知,无法进行编译。对于编译器来说,模板实现也是一种声明,声明如何自动生成代码。所以模板的实现也应该放在头文件内,这样,在其他代码文件中可以直接将模板的实现也包含进来,当需要生成模板实例时,编译器可根据已知模板实现当场生成,而无需依赖在别的目标文件中生成的模板实例。

但这样会带来另一个问题,即重复模板实例。

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇1.2.5 模板函数的静态变量 下一篇1.3.2 链接器如何识别重复模板实..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: