天天看点

显式的实例化与外部模板的声明

2.12.2 显式的实例化与外部模板的声明

《深入理解C++11:C++11新特性解析与应用》第2章保证稳定性和兼容性,本章中的新特性基本上都遵循了该设计思想。本节为大家介绍显式的实例化与外部模板的声明。

作者:Michael Wong IBM XL编译器中国开发团队来源:机械工业出版社|2013-06-24 13:50

 收藏

  分享

开发者盛宴来袭!7月28日51CTO首届开发者大赛决赛带来技术创新分享

2.12.2 显式的实例化与外部模板的声明

外部模板的使用实际依赖于C++98中一个已有的特性,即显式实例化(Explicit Instantiation)。显式实例化的语法很简单,比如对于以下模板:

  1. template <typename T> void fun(T) {} 

我们只需要声明:

  1. template void fun<int>(int); 

这就可以使编译器在本编译单元中实例化出一个fun<int>(int)版本的函数(这种做法也被称为强制实例化)。而在C++11标准中,又加入了外部模板(Extern Template)的声明。语法上,外部模板的声明跟显式的实例化差不多,只是多了一个关键字extern。对于上面的例子,我们可以通过:

  1. extern template void fun<int>(int); 

这样的语法完成一个外部模板的声明。

那么回到一开始我们的例子,来修改一下我们的代码。首先,在test1.cpp做显式地实例化:

  1. #include "test.h"  
  2. template void fun<int>(int); // 显示地实例化  
  3. void test1() { fun(3); } 

接下来,在test2.cpp中做外部模板的声明:

  1. #include "test.h"  
  2. extern template void fun<int>(int); // 外部模板的声明  
  3. void test1() { fun(3); } 

这样一来,在test2.o中不会再生成fun<int>(int)的实例代码。整个模板的实例化流程如图2-2所示。

显式的实例化与外部模板的声明

可以看到,由于test2.o不再包含fun<int>(int)的实例,因此链接器的工作很轻松,基本跟外部变量的做法是一样的,即只需要保证让test1.cpp和test2.cpp共享一份代码位置即可。而同时,编译器也不用每次都产生一份fun<int>(int)的代码,所以可以减少编译时间。这里也可以把外部模板声明放在头文件中,这样所有包含test.h的头文件就可以共享这个外部模板声明了。这一点跟使用外部变量声明是完全一致的。

在使用外部模板的时候,我们还需要注意以下问题:如果外部模板声明出现于某个编译单元中,那么与之对应的显示实例化必须出现于另一个编译单元中或者同一个编译单元的后续代码中;外部模板声明不能用于一个静态函数(即文件域函数),但可以用于类静态成员函数(这一点是显而易见的,因为静态函数没有外部链接属性,不可能在本编译单元之外出现)。

在实际上,C++11中“模板的显式实例化定义、外部模板声明和使用”好比“全局变量的定义、外部声明和使用”方式的再次应用。不过相比于外部变量声明,不使用外部模板声明并不会导致任何问题。如我们在本节开始讲到的,外部模板定义更应该算作一种针对编译器的编译时间及空间的优化手段。很多时候,由于程序员低估了模板实例化展开的开销,因此大量的模板使用会在代码中产生大量的冗余。这种冗余,有的时候已经使得编译器和链接器力不从心。但这并不意味着程序员需要为四五十行的代码写很多显式模板声明及外部模板声明。只有在项目比较大的情况下。我们才建议用户进行这样的优化。总的来说,就是在既不忽视模板实例化产生的编译及链接开销的同时,也不要过分担心模板展开的开销。

继续阅读