简介
C++ 中的模板(一):函数模板 对函数模板做了简单的整理。这篇博客主要对类模板的相关概念也进行整理,相较于函数模板,类模板在 STL 中使用更为广泛。
类模板实例化
和函数模板类似,类模板本身不是任何实体,只是用以产生类的模板,因此本身不会产生任何汇编代码,只有通过实例化才能产生实际的代码。因此为了实例化我们需要提供对应的实例类的声明和定义。
显式实例化
实例化有两种方式:显式实例化和隐式实例化。显式实例化为在编译单元中显式地提供模板类的定义式或声明式:
template <typename T>
class Base {
public:
Base(T t) : t(t) {}
private:
T t;
};
template class Base<int>; // 显式提供定义式
int main() { return 0; }
编译后汇编代码为:
Base<int>::Base(int) [base object constructor]:
mov DWORD PTR [rdi], esi
ret
main:
xor eax, eax
ret
当我们不需要编译器在本编译单元中提供实现时,我们可以只进行声明而不定义,如下所示:
template <typename T>
class Base {
public:
Base(T t) : t(t) {}
private:
T t;
};
extern template class Base<int>; // 模板类声明
int main() { return 0; }
此时编译后不会产生模板类的汇编代码:
main:
xor eax, eax
ret
隐式实例化
对于类模板而言,隐式实例化稍微复杂一点,当代码涉及到类模板,且满足以下条件时,隐式实例化会被触发,编译器会对涉及到的实例对模板进行实例化:
- 该代码需要完整定义(completely defined)的类型或类型的完整性影响代码时
- 这个特定类型没有被显式实例化时(包括显式定义或者显式声明)
下面来看一些例子:
template <typename T>
class Base {
public:
Base(T t) : t(t) {}
void func1() { t = t + T(1.0); }
void func2();
private:
T t;
};
int main() {
Base<int> b_int(1);
Base<float>* pb_float;
Base<double>* pb_double = new Base<double>(10.0);
pb_double->func1();
return 0;
}
下面分别来看一下:
Base<int> b_int(1);
这里,b_int
被初始化,因此需要Base<int>::Base()
的定义,其本身没有被显式实例化,因此触发隐式实例化,Base<int>
的构造函数被实例化Base<float>* pb_float;
这里声明了一个Base<float>
的类型的指针,但是由于没有进行初始化,即不需要Base<float>
的构造函数,因此没有函数被初始化Base<double>* pb_double = new Base<double>(10.0);
: 和b_int
类似,Base<double>
的构造函数被初始化pb_double->func1();
:Base<double>::func1()
被调用因此被隐式初始化,Base<double>::func2()
没被调用,因此即使没被定义也不受影响
相关的汇编代码如下所示:
不完整的类(只有声明没有定义)在实例化时会报编译错误。
类模板特例化
和函数模板一样,类模板也可以进行特例化,如下所示:
#include <iostream>
template <typename T>
class Base {
public:
Base() { std::cout << "General base.\n"; }
};
template <>
class Base<int> {
public:
Base() { std::cout << "Specialized base.\n"; }
};
int main() {
Base<int> b_int;
Base<double> b_double;
return 0;
}
运行结果为:
$ /home/xt/code_collections/cpp/build/templates/class_templates/class_template_example4
Specialized base.
General base.
在某些情况下,我们可能需要对嵌套模板类进行特例化,这个时候要求,如果我们需要对嵌套部分中某个类/函数进行特例化,那么它的外围类必须也要特例化,如下所示:
#include <iostream>
template <typename T1>
class Outter {
public:
Outter() { std::cout << "General Outter.\n"; }
template <typename T2>
class Inner {
public:
Inner() { std::cout << "General Inner.\n"; }
};
};
template <> // Outter 特例化
template <typename T> // Inner 保持原样
class Outter<int>::Inner {
public:
Inner() { std::cout << "Specialized Inner.\n"; }
};
int main() {
Outter<int>::Inner<int> a;
Outter<double>::Inner<int> b;
return 0;
}
运行结果为:
$ /home/xt/code_collections/cpp/build/templates/class_templates/class_template_example4
Specialized Inner.
General Inner.
相比于函数模板,类模板还可以进行偏特化,即有多个模板参数时,可以只特例化其中部分参数,如下所示:
#include <boost/type_index.hpp>
#include <iostream>
template <typename T1, typename T2>
class Base {
public:
Base() {
std::cout << "General: T1 = " << boost::typeindex::type_id<T1>().pretty_name()
<< ", T2 = " << boost::typeindex::type_id<T2>().pretty_name() << std::endl;
}
};
template <typename T>
class Base<int, T> {
public:
Base() { std::cout << "Specialzied: T1 = int, T2 = " << boost::typeindex::type_id<T>().pretty_name() << std::endl; }
};
int main() {
Base<int, double> a;
Base<double, double> b;
return 0;
}
运行结果如下所示:
$ /home/xt/code_collections/cpp/build/templates/class_templates/class_template_example6
Specialzied: T1 = int, T2 = double
General: T1 = double, T2 = double