一、说明
模板的显式实例化,explicit instantiation,也叫全实例化。所谓显式实例化,就是开发者为模板代码(模板函数或类)手动编写指定类型的代码。类似下面的代码:
template<typename T>
T Demo(T a) {
return a;
}
template int Demo<int>(int);
template std::string Demo<std::string>(std::string);
大家一定要把显式实例化和全特化区分出来,一个最简单的方法是,显式实例化的template后不再跟着模板尖括号“<>”,而所有的模板和特化(不管是偏特化和全特化)都是要有的。看一下全特化的情况:
template<typename T>
T Demo(T a) {
return a;
}
template<>
int Demo(int a){
return a;
}
也可以理解为全实例化只需声明不用自写实现而全特化需要自己实现相关的代码。当然,模板类也是如此实现的,此处不再给出例子。
二、显式实例化和隐式实例化
C++模板默认的是使用的隐式实例化即Implicit Instantiation,由于模板通常是定义在头文件中的,所以在实际生成代码中,会生成多个实例,这就需要编译器和链接器在实际的处理中进行去重的处理。而显式实例化则直接给出了模板最终的生成形式,并由编译器最终生成代码。这种情况下,基本等同于一个明确的模板实现代码(类或函数),或者说最终形成的代码已经可以不认为是模板代码了。这就出现了一个问题,如果其在头文件中,则必然产生重复定义的结果。所以,显式实例化的代码必须在cpp文件中,这是与普通模板代码一般在头文件中的一个最明显的不同。
而从编译器的角度来看,显式实例化由于在cpp文件中,一则避免了编译器对普通模板代码在头文件中编译导致的代码膨胀的问题;二则也解决了链接器中的去重处理。
三、应用场景和注意点
一般来说,显式实例化的应用非常广泛,不过也只是对模板开发来说。其主要的应用场景为:
- 库和框架的开发
其实大家非常容易理解,由于模板开发的复杂性和难度,一般都是在底层框架中应用,最典型的就是STL和BOOST之类的库中。 - 有针对性的控制模板的应用类型
这个可以理解,在对外的接口或应用中,可以将特定类型指定为应用类型 - 特定代码优化
比如某些库或模板代码需要减少模板的代码膨胀和提高链接效率(去重)等 - 支持新标准
如C++11后的外部模板,extern template。关于外部模板,前面分析过,此处不再展开
需要注意的是,使用显式实例化,必须优先定义普通模板,即显式实例化的模板必须可以在编译期搜索到普通模板的定义,否则会报编译错误;另外其不适用于局部类和匿名类(这也符合模板开发的要求)。
四、例程
下面给出一个简单的例程,然后将其显式实例化的代码编译出来:
#include <string>
template<typename T>
T add(T a, T b) { return a + b; }
template int add<int>(int, int);
template float add<float>(float, float);
int main() {
std::string s1 = "1";
std::string s2 = "2";
std::string s = add(s1,s2);
return 0;
}
编译后的代码是:
#include <string>
template<typename T>
T add(T a, T b)
{
return a + b;
}
/* First instantiated from: insights.cpp:6 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
int add<int>(int a, int b)
{
return a + b;
}
#endif
/* First instantiated from: insights.cpp:7 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
float add<float>(float a, float b)
{
return a + b;
}
#endif
/* First instantiated from: insights.cpp:11 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
std::basic_string<char> add<std::basic_string<char> >(std::basic_string<char> a, std::basic_string<char> b)
{
return std::operator+(a, b);
}
#endif
int main()
{
std::basic_string<char> s1 = std::basic_string<char>("1", std::allocator<char>());
std::basic_string<char> s2 = std::basic_string<char>("2", std::allocator<char>());
std::basic_string<char> s = add(std::basic_string<char>(s1), std::basic_string<char>(s2));
return 0;
}
在上面的代码和编译后的代码进行对比可以看出来,显式实例化的代码会自动生成指定数据类型的模板实例(具现)化的代码。大家可以将模板类也以上面的形式进行处理一下,看最终的代码会是什么样。给出一个小例子,可以在此代码基础上完善:
template<typename T>
class Example {
public:
void add(const T& x){};
void count(){};
};
// 显式实例化
template class Example<int>;
template class Example<char>;
五、总结
模板的学习对于开发者来说不是一个一蹴而就的问题,需要开发者不断的将书本知识与实践反复融合。而且,随着C++最新的标准不断的完善,针对模板的相关内容(如概念等)和技术也在不断的完善,这就对开发者提出了更高的要求。不过,不要着急,休息一下,休息一下。

2747





