函数模板是通用的函数描述,使用任意类型(泛型)来描述函数。
编译的时候,编译器推导实参的数据类型,根据实参的数据类型和函数模板,生成该类型的函数定义。
生成函数定义的过程被称为 实例化。
创建交换两个变量的函数模板:
template <typename T>
void Swap(T& a, T& b) {
T tmp = a;
a = b;
b = tmp;
}
在C++98添加关键字 typename
之前,C++使用关键字 class
来创建模板。
如果考虑向后兼容,函数模板应使用 typename
,而不是 class
。
函数模板实例化可以让编译器自动推导,也可以在调用的代码中显式指定。
int a, b;
double c, d;
swap(a,b); // 自动类型推导
swap<double>(c,d); // 显式指定类型
注意
- 可以为类的成员函数创建模板,但不能是虚函数和析构函数。
- 自动类型推导时,必须推导出一致的数据类型T才可以使用。
- 使用函数模板时,必须明确T的数据类型,确保实参与函数模板能匹配上。
- 使用函数模板时,推导的数据类型必须适应函数模板中的代码。
- 使用函数模板时,如果是自动类型推导,不会发生隐式类型转换,如果显式指定了函数模板的数据类型,可以发生隐式类型转换。
- 函数模板支持多个通用数据类型的参数。
- 函数模板支持重载,可以有非通用数据类型的参数。
example
notice 2
template <typename T>
void swap(T& a, T& b) {
T tmp = a;
a = b;
b = tmp;
}
void test() {
int a = 0;
char c = 'c';
swap(a, c); // error
}
notice 3
template <typename T>
void func() {
std::cout << "func" << std::endl;
}
void test1() {
func(); // error 不能确定T的类型
}
void test2() {
func<int>(); // ok 显式指定T类型
}
类模板是通用类的描述,使用任意类型(泛型)来描述类的定义。
使用类模板的时候,指定具体的数据类型,让编译器生成该类型的类定义。
语法
template <class T>
class Name
{
...
};
函数模板建议用 typename
描述通用数据类型,类模板建议用 class
注意
- 在创建对象的时候,必须指明具体的数据类型,不可以使用自动类型推导。
- 使用类模板时,数据类型必须适应类模板中的代码。
- 模板类的成员函数可以在类外实现。
- 可以用new创建模板类对象。
- 在程序中,模板类的成员函数使用了才会创建。
示例
template <class T1, class T2>
class A {
public:
T1 m_a;
T2 m_b;
A() {}
AA(T1 a, T2 b)
: m_a(a), m_b(b) {}
T1 a() {
T1 a = 2;
return m_a + a;
}
T2 b();
};
template <class T1, class T2>
T2 A<T1,T2>::b() {
return m_b;
}
int main()
{
A<int, std::string>* a = new AA<int, std::string>(3, "hello");
std::cout << "a->a()=" << a->a() << std::endl;
std::cout << "a->b()=" << a->b() << std::endl;
delete a;
}
此时实例化模板时 必须指明具体的数据类型
class A {
public:
A() {}
A(int a)
: _a(a) {}
int _a;
};
template <typename T>
void func() {
T a(1);
std::cout << a._a << std::endl;
}
template <class T>
class B {
public:
T obj;
B() {
obj._a = 1;
std::cout << obj._a << std::endl;
}
};
int main() {
func<A>();
B<A> b;
}
output
1
1
一般在类模板中使用,C++11起支持函数模板使用默认参数,且 不需要遵循 从右向左赋默认值的规则
template <typename T = int>
class A {
public:
T a;
};
以下用法只是先告诉你它存在,但是具体怎么用可能以后不会告诉你,等你自己去探索咯
template<typename, typename T>
template<typename T, T i, int j>
可以提供一个特化的函数定义,当编译器找到与函数调用匹配的特化定义时,将使用该定义,不再寻找模板。
模板特化时,必须先写 主模板 (primary template) ,再写特化模板
语法
// 主模板
template<>
void function<Type>(param)
// 模板特化
template<>
void function(param) {
...
}
对于给定的函数名,可以有普通函数、函数模板和具体化的函数模板,以及它们的重载版本。
编译器使用各种函数的规则:
- 普通函数 > 特化模板 > 常规模板
- 如果希望使用函数模板,可以用空模板参数强制使用函数模板
- 如果函数模板能产生更好的匹配,将优先于普通函数
示例
template <typename T>
void Swap(T& a, T& b) {
T tmp = a;
a = b;
b = tmp;
std::cout << "Swap(T& a, T& b)" << std::endl;
}
template <>
void Swap<int>(int& a, int& b) { // <int>可以省略
int tmp = a;
a = b;
b = tmp;
std::cout << "Swap<int>(T& a, T& b)" << std::endl;
}
int main() {
double a = 10, b = 20;
Swap(a, b);
std::cout << "a=" << a << ",b=" << b << std::endl;
int n1 = 10, n2 = 20;
Swap(n1, n2);
std::cout << "n1=" << n1 << ",n2=" << n2 << std::endl;
}
output
Swap(T& a, T& b)
a=20,b=10
Swap<int>(T& a, T& b)
n1=20,n2=10
void Swap(int a, int b) {
std::cout << "普通函数" << std::endl;
}
template <typename T>
void Swap(T a, T b) {
std::cout << "模板函数" << std::endl;
}
template <>
void Swap<int>(int a, int b) {
std::cout << "特化函数模板" << std::endl;
}
int main() {
Swap('c', 'd');
Swap(1, 2);
Swap<>(1, 2);
}
output
模板函数
普通函数
特化函数模板
模板类特化分为 完全特化 和 部分特化
调用顺序
特化程度高的类优先于特化程度低的类,特化的类优先于没有特化的类。
// 主模板
template <class T1, class T2>
class AA {
public:
T1 m_x;
T2 m_y;
AA(const T1 x, const T2 y)
: m_x(x), m_y(y) {
std::cout << "primary template constructor" << std::endl;
}
void show() const;
};
template <class T1, class T2>
void AA<T1, T2>::show() const {
std::cout << "primary template\nx = " << m_x << ", y = " << m_y << std::endl;
}
// 完全特化
template <>
class AA<int, double> {
public:
int m_x;
double m_y;
AA(const int x, const double y)
: m_x(x), m_y(y) {
std::cout << "complete specialization constructor" << std::endl;
}
void show() const;
};
void AA<int, double>::show() const {
std::cout << "complete specialization\nx = " << m_x << ", y = " << m_y << std::endl;
}
// 部分特化
template <class T1>
class AA<T1, double> {
public:
T1 m_x;
double m_y;
AA(const T1 x, const double y)
: m_x(x), m_y(y) {
std::cout << "partial specialization constructor" << std::endl;
}
void show() const;
};
template <class T1>
void AA<T1, double>::show() const {
std::cout << "partial specialization\nx = " << m_x << ", y = " << m_y << std::endl;
}
int main() {
// 特化程度高的类优先于特化程度低的类,特化的类优先于没有特化的类
AA<int, double> aa1(8, 1.1);
AA<char, double> aa2(8, 1.1);
AA<int, double> aa3(8, 'a');
}
output
complete specialization constructor
partial specialization constructor
complete specialization constructor
template<typename T> class XXX<T[]>{};
暂时想不到应用场景,先告诉你存在这样的用法吧
edit Serein