转载自:C++11模板隐式实例化、显式实例化声明、定义(简单易懂)_云飞扬_Dylan的博客-CSDN博客_模板隐式实例化
1. 隐式实例化
在代码中实际使用模板类构造对象或者调用模板函数时,编译器会根据调用者传给模板的实参进行模板类型推导然后对模板进行实例化,此过程中的实例化即是隐式实例化。
注:个人理解:模板类、模板函数的模板类型也是对应类、函数的“类型参数”,调用模板类、模板函数时将根据传入的“模板类型参数”才能实例化完整的类、完整的函数,不同的“模板类型参数”能实例化出不同的类、函数。
template
T add(T t1, T t2)
{
return t1 + t2;
}
template
class Dylan
{
public:
T m_data;
};
int main()
{
int ret = add(3,4);//隐式实例化,int add
Dylan
}
注:个人理解:隐式实例化模板函数可以不用add
2. 显式实例化声明、定义
extern template int add
extern template class Dylan
template int add
template class Dylan
当编译器遇到显式实例化声明时,表示实例化定义在程序的其他地方(相对于当前cpp文件)即在其他某一个cpp文件中定义,因此不再按照模板进行类型推导去生成隐式实例化定义。
当编译器遇到显式实例化定义时,根据定义所提供的模板实参去实例化模板,生成针对该模板实参的实例化定义。
3. 显式实例化的用途
模板类、函数通常定义在头文件中,这些头文件会被很多cpp文件包含,在这些cpp文件中会多次使用这些模板,比如下面的例子:
//template.hpp
template
class Dylan
{
public:
T m_data;
};
//test1.cpp
#include "template.hpp"
Dylan
Dylan
//test2.cpp
#include "template.hpp"
Dylan
Dylan
在test1.cpp/test2.cpp 中多次实例化了Dylan
a. Borland模式
Borland模式通过在编译器中加入与公共块等效的代码来解决模板实例化问题。在编译时,每个文件独立编译,遇到模板或者模板的实例化都不加选择地直接编译。在链接的时候将所有目标文件中的模板定义和实例化都收集起来,根据需要只保留一个。这种方法实现简单,但因为模板代码被重复编译,增加了编译时间。在这种模式下,我们编写代码应该尽量让模板的所有定义都放入头文件中,以确保模板能够被顺利地实例化。要支持此模式,编译器厂商必须更换支持此模式的链接器。
b. Cfront模式
AT&T编译器支持此模式,每个文件编译时,如果遇到模板定义和实例化都不直接编译,而是将其存储在模板存储库中(template repository)。模板存储库是一个自动维护的存储模板实例的地方。在链接时,链接器再根据实际需要编译出模板的实例化代码。这种方法效率高,但实现复杂。在这种模式下,我们应该尽量将非内联成员模板的定义分离到一个单独的文件中,进行单独编译。
在一个链接器支持Borland模式的编译目标(编译后的可执行文件)上,g++使用Borland模式解决实例化问题。比如ELF(Linux/GNU), Mac OS X, Microsoft windows, 否则,g++不支持上述两种模式。
如何避免Borland模式的缺点?
上面我们说g++实现的是Borland 模式,由于我们为每一份实例化生成代码,这样在大型程序中就有可能包含很多重复的实例化定义代码,虽然链接阶段,链接器会剔除这些重复的定义,但仍然会导致编译过程中的目标文件(或者共享库文件)过于庞大。这时候,我们就可以通过C++11的模板显式实例化的方法解决。看下面的代码:
// template.hpp
template
class Dylan
{
public:
Dylan(T t);
T m_data;
};
// template.cpp
#include "template.hpp"
template
Dylan
{
m_data = t;
}
template class Dylan
// main.cpp
#include "template.hpp"
extern template class Dylan
//不需要根据模板定义生成实例化代码
int main()
{
Dylan
Dylan
}
上面的代码中,我们将模板类的具体定义放在template.cpp中,并且在template.cpp中通过显式实例化定义语句具体化了Dylan
由于我们没有针对Dylan
注:补充一点,在一个cpp中extern声明的变量、函数将在链接阶段前往其他cpp进行查找链接,在一个cpp中调用classA.h中声明的方法,在链接阶段也会前往classA.cpp中查找链接而不用在classA.h中extern声明,这是链接发生的两个情形;
Note:在编译中,如果指定-fno-implicit-templates,编译器就会禁止隐式实例化,从而只使用显式实例化。
参考文献:Template Instantiation (Using the GNU Compiler Collection (GCC))
————————————————
版权声明:本文为CSDN博主「云飞扬_Dylan」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Jxianxu/article/details/124359007