模板¶
1 主模板¶
如果要对模板进行特化,那么必须先要声明主模板
template<class T> class X; // 主模板(声明或者定义都可以)
template<> class X<int> { /*...*/ }; //模板特化
2 模板形参与实参¶
- 类型 名字(可选) (1)
- 类型 名字(可选) = 默认值 (2)
- 类型 ... 名字(可选) (3)
注意:模板形参的名字是可以省略的
2.1 非类型模板形参¶
2.1.1 基本类型和枚举类型形参¶
enum EN {
EN_1=0
};
template<typename T,int sz>
class A
{
public:
T array[sz];
};
template<EN en>
class A1
{
public:
void fun()
{
switch (en)
{
case EN_1:
printf("EN_1");
break;
default:
break;
}
}
};
2.1.2 函数指针形参¶
void print1(int i)
{
printf("%d", i);
}
template<void(*f)(int)>
void fun1(int i)
{
f(i);
}
template<typename T>
void print2(T i)
{
printf("%d", i);
}
template<typename T,void(*f)(T)>
void fun2(T i)
{
f(i);
}
int main()
{
fun1<print1>(1);
fun2<int, print1>(2);
fun2<int, print2>(3.3);
}
2.2 参考资料¶
3 类模板¶
3.1 模板具体化¶
- 隐式实例化
- 解释:当使用具体的模板时,如有语句
vector<int> vi;
,则编译器会隐式生成vector<int>
的定义,并实例化vi
对象 - 显式实例化
- 关键字:
template
- 解释:通过
template
关键字直接告诉编译器要生成具体的类,如template class vector<int>;
,此时编译器会生成vector<int>
的具体类的声明。 - 显式具体化
- 关键字:
template<>
- 解释:为某一类型特化的类,形如
template<> class_name<type_name>
- 部分具体化
- 解释:如多个模板参数的情况下,只具体化其中部分,形如
template<class T> class_name<type_name,T>
显式实例化和隐式实例化不同之处在于,隐式实例化需要声明一个对象,如上面提及的vi
// 一般模板
template<class T1,class T2>
class A {
public:
void print() { cout << "A<T1,T2>" << endl; }
};
// 显式具体化
template<>
class A<int,int> {
public:
void print() { cout << "A<int,int>" << endl; }
};
// 部分具体化
template<class T>
class A<T,int> {
public:
void print() { cout << "A<T,int>" << endl; }
};
// 显式实例化
template class A<string, float>;
int main()
{
//隐式实例化
A<float,float> af;
af.print();//A<T1,T2> 调用一般模板
A<int,int> ai;
ai.print();//A<int,int> 调用具体化模板A<int,int>
A<float, int> afi;
afi.print();//A<T,int> 调用部分具体化模板A<T,int>
}
3.2 类模板类型¶
- 非类型参数模板
- 默认模板参数
- 解释:可以为任何种类的模板形参(类型、非类型或模板)指定默认实参,如
template<typename T1, typename T2 = int> class A;
- 模板作为参数
template<typename T>
class A1 {
public:
using _TYPE = T;
_TYPE m_a;
//A1():m_a(){}
A1() = default;
A1(const T& a):m_a(a){}
A1& operator =(A1& a)
{
m_a=a.m_a;
return *this;
}
};
//模板作为参数
template<template<typename T>class A>
class B {
public:
};
//非类型参数模板,默认模板参数
template<typename T,int n=2>
class D
{
public:
D() :value(){}
T value[n];
};
int main()
{
D<string,3> arr;
B<A1> b1;
}
3.3 成员函数模板¶
- 模板类中函数模板
- 普通类中函数模板
//类模板中函数模板
template<typename T>
class E
{
public:
using _TYPE = T;
_TYPE _value;
//类内实现
template<typename U>
void func1(U u){}
//类内声明
template<typename U>
void func2(U u);
template<typename X>
class cl {
public:
X x;
};
cl<T> c1_T;
cl<int> cl_i;
};
//类外实现 (双层template)
template<typename T>
template<typename U>
void E<T>::func2(U u){}
//普通类中函数模板
class F
{
public:
//类内实现
template<typename T>
void fun1(T t){}
//类内声明
template<typename T>
void fun2(T t);
};
//类外实现
template<typename T>
void F::fun2(T t){}
3.4 模板类中自身类指针¶
template<typename T>
class G
{
public:
G() :pg2(nullptr),pg1(nullptr) {}
G* pg1;//默认与G<T>类型相同
G<T>* pg2;//等价上式
};
3.5 友元模板类和友元模板函数¶
- 知识点:
- 友元模板类不需要前置声明
template<typename T>
class H
{
T val;
public:
void setValue(T v) { val = v; }
H():val(T()){}
//友元模板类
template<typename U>
friend class L;
//友元函数
template<typename ...U>
friend auto sum(U... args)->decltype((args.val + ...));
};
template<typename T>
class L
{
};
template<typename...T>
auto sum(T...args)->decltype((args.val + ...))
{
//c++17折叠表达式语法
return (args.val + ...);
}
int main()
{
H<int> h1;
h1.setValue(2);
H<int> h2;
h2.setValue(3);
sum(h1,h2);//输出5
}
4 函数模板¶
4.1 模板具体化¶
- 隐式实例化
- 解释:当使用具体的模板时,如有语句
int a=2,b=1; swap(a,b);
,则编译器会隐式生成void swap(int&,int&)
的定义 - 显式实例化
- 关键字:
template
- 解释:通过
template
关键字直接告诉编译器要生成具体的函数,如template void swap(int&,int&)
,此时编译器会生成void swap(int&,int&)
的函数定义。 - 显式具体化
- 关键字:
template<>
- 解释:为某一类型特化的函数,形如
template<> ret_type fun_name(type_name)
- 部分具体化
- 解释:如多个模板参数的情况下,只具体化其中部分,形如
template<class T> ret_type fun_name(type_name,T)
template<class T1,class T2>
void print(T1& t1, T2& t2) {
cout <<"一般模板" <<endl;
}
template< class T>
void print(int& t1, T& t2) {
cout << "部分具体化"<< endl;
}
template<>
void print(int& t1, int& t2) {
cout << "显式具体化" << endl;
}
//显式实例化
template void print<double,double>(double&, double&);//在不调用此函数时,此函数已被声明
int main()
{
float f = 1.1;
double d = 2.1;
int i = 1;
print(i, f);//部分具体化
print(i, i);//显式具体化
print(f, f);//一般模板
print(d, d);//一般模板
}
5 别名模板¶
- 功能:简化复杂模板,特殊化模板参数
#include<array>
template<class T>
using arr10 = array<T, 10>;
int main()
{
arr10<int> arr{1,2,3};
}
6 变量模板¶
- 功能:变量模板定义一族变量或静态数据成员
// 全局作用域
template <class>
_INLINE_VAR constexpr bool is_lvalue_reference_v = false; // determine whether type argument is an lvalue reference
template <class _Ty>
_INLINE_VAR constexpr bool is_lvalue_reference_v<_Ty&> = true;
template <class>
_INLINE_VAR constexpr bool is_rvalue_reference_v = false; // determine whether type argument is an rvalue reference
template <class _Ty>
_INLINE_VAR constexpr bool is_rvalue_reference_v<_Ty&&> = true;
template<class T>
constexpr T PI = T(3.1415926);
//类作用域
struct limits {
template<typename T>
static const T min; // 静态数据成员模板的声明
};
template<typename T>
const T limits::min = { }; // 静态数据成员模板的定义
template<class T>
class X {
public:
static T s; // 类模板的非模板静态数据成员的声明
const static T s1=0; // 常量可以直接初始化
};
template<class T>
T X<T>::s = 0; // 类模板的非模板静态数据成员的定义
int main()
{
bool b1 = is_lvalue_reference_v<int>;//false
bool b2 = is_lvalue_reference_v<int&>;//true
bool b3 = is_rvalue_reference_v<int>;//false
bool b4 = is_rvalue_reference_v<int&&>;//true
int i=PI<int>;
float f = PI<float>;
int i1=limits::min<int>;
int i2 = X<int>::s;
}
7 待决名的typename和template消除歧义符¶
在一些源码中可能有这样的语法using _Ty2 = typename _Select<is_function_v<_Ty1>>::template _Apply<add_pointer<_Ty1>, remove_cv<_Ty1>>;
,其中的typename
和template
到底起着什么作用呢?
解释如下:
- typename
在此用于说明这这_Select<is_function_v<_Ty1>>
是一个类型
- template
在此说明_Apply
是一个模板
typename
示例
struct A { // A 拥有嵌套变量 X 和嵌套类型 struct X
struct X {};
int X;
};
struct B {
struct X { }; // B 拥有嵌套类型 struct X
};
template<class T> void f(T t) {
typename T::X x;
}
void foo() {
A a;
B b;
f(b); // OK:实例化 f<B>,T::X 指代 B::X
f(a); // 错误:不能实例化 f<A>:
// 因为 A::X 的有限定名字查找找到了数据成员
}
template
示例
template<typename T>
struct S {
template<typename U> void foo(){}
};
template<typename T>
void bar()
{
S<T> s;
s.foo<T>(); // 错误:< 被剖析为小于运算符
s.template foo<T>(); // OK
}
关键词 template 仅可以这种方式用于运算符 ::(作用域解析)、->(通过指针的成员访问)和 .(成员访问)之后,下列表达式是所有合法示例:
T::template foo<X>();
s.template foo<X>();
this->template foo<X>();
typename T::template iterator<int>::value_type v;
更详细资料可参考cppreference