一、什么是外部模板
模板的优势估计很多人都知道,但是模板的劣势也有不少,其中,代码膨胀就是其中之一(其它还有不易理解、不易调试等)。在实际情况中可能会有下面这种情况:
//tempFun.h
#pragma once
template<typename T,typename N>
T Add(T t, N n)
{
return t + n;
}
//test.h
#pragma once
void First(int a,int b);
void Second(int a,int b);
//first.cpp
#include "iostream"
#include "tempFun.h"
#include "test.h"
void First(int a, int b)
{
std::cout << Add(a,b) << std::endl;
}
//second.cpp
#include "iostream"
#include "tempFun.h"
#include "test.h"
void Second(int a, int b)
{
std::cout << Add(a, b) << std::endl;
}
//main.cpp
#include "test.h"
#include <Windows.h>
int main()
{
First(10,1);
Second(11,2);
system("pause");
return 0;
}
上面的示例代码非常简单,但可以说明一个问题,在上一篇中提到过,c++的编译是以CPP为单元进行的,模板如果不使用就不会生成代码,那么如果在两个CPP文件中使用一样的数据类型会产生什么样的情况呢?比如上在这种情形,都是int类型,只是数据略有不同。答案是编译器肯定会生成两个完全相同的函数。然后在后续的过程中(包含链接等)会通过技术手段去除重复的代码,来实现单一副本。但是,扩展开来,如果一个工程非常大,编译器等经常做类似的工作,对生成效率的影响,估计可以想象的到。
学习c++/c语言的人,都会接触到extern关键字,知道一个全局变量可以通过其来进行外部声明。同样,在模板中,也可以使用这种方式。在c++11中引用了外部模板,就是通过这个关键字来解决这个问题的。
二、外部模板使用
在c++11中引入的外部模板,类似于extern一个全局变量的编译链接过程,如果发现这个关键字,就不再本地再来一份生成代码了。这样,就减少了前面提到的单一副本的处理过程了。看一个实例:
//其它类似,只修改使用的两个CPP
//first.cpp
#include "iostream"
#include "tempFun.h"
#include "test.h"
template int Add<int,int>(int,int);
void First(int a, int b)
{
std::cout << Add(a,b) << std::endl;
}
//second.cpp
#include "iostream"
#include "tempFun.h"
#include "test.h"
extern template int Add<int,int>(int,int);
void Second(int a, int b)
{
std::cout << Add(a, b) << std::endl;
}
上面的代码可以看出,其实是利用了显示实例化来实现这个问题,在以前的文章中说过,显示实例化只能一次,而且要放到CPP文件中,这里与之类似。首先在一个编译单元中将其显示实例化,然后在另外一个编译单元中使用extern关键字来声明其它单元中已经生成了这个代码。不过和全局变量相同,必须保证有实际的定义之处,否则编译会报链接错误。
需要注意的是,在一些IDE的开发环境中,会发现extern部分的函数会提示找不到实现,这是因为模板需要在编译期才会生成相关的实现代码,恰恰说明了上面的问题。如果大家有兴趣,也可以自己实验一番。
外部模板的使用有一些限制场景:
1、可以用于类内的静态函数,而不能用于全局的静态函数。
2、目前对内联的支持看编译器的实现。
3、外部模板只支持类成员而不支持类本身。
三、总结
其实从实际情况来看,决大多数的工程其实对这个外部模板是不感冒的,因为,其工程的大小可控。这就使得这个东西变成了鸡肋,所以说,对其的使用,还是根据自己的实际的情况来处理。说的直白一些,就是平衡效率和修改使用的工作量。
《2019-10-13 09:27》