🔥🔥 欢迎来到小林的博客!!
🛰️博客主页:✈️林 子
🛰️博客专栏:✈️ C++
🛰️社区 :✈️ 进步学堂
🛰️欢迎关注:👍点赞🙌收藏✍️留言
目录
- 认识模板
- function template函数模板
- class类模板
- 模板特化
- 半特化(偏特化)
- 模板原理
认识模板
模板是C++中泛型编程的基础,一个模板就是一个创建类或者函数的蓝图或者说是公式。
当使用一个vector这样的泛型类型时,或者find这样的泛型函数时,我们提供足够的信息,将蓝图转化成类或者函数。这种转化发生在编译时。
function template函数模板
比如我们想写一个swap函数来交换两个数的值,在C++中我们可以用函数重载来重载多个函数来实现对不同类型的交换。比如我们要交换int 和 double类型,那么仅仅重载一个 swap(int& x ,int& y)是不够的。还需要重载一个swap(double& x,double& y) 函数。 但如果我还想交换其他的类型,也要写出其他类型对应的函数重载。这样就显得十分麻烦。所以这时候我们就可以function template(函数模板) 来减少我们需要写的代码。
template<class T> //定义函数模板
void Swap(T& x, T& y) //T是模板参数,也就是你传入的参数
{
T tmp = x;
x = y;
y = tmp;
}
int main()
{
int i1 = 5, i2 = 10;
double d1 = 5.5, d2 = 10.5;
string str1 = "hello", str2 = "world";
cout << "交换前:" << endl;
cout << "i1 : " << i1 << " , i2 :" << i2 << endl;
cout << "d1 : " << d1 << " , d2 :" << d2 << endl;
cout << "str1 : " << str1 << " , str2 :" << str2 << endl;
Swap(i1, i2);
Swap(d1, d2);
Swap(str1, str2);
cout << "交换后:" << endl;
cout << "i1 : " << i1 << " , i2 :" << i2 << endl;
cout << "d1 : " << d1 << " , d2 :" << d2 << endl;
cout << "str1 : " << str1 << " , str2 :" << str2 << endl;
return 0;
}
代码运行结果:
我们写了一份代码,就可以交换所有类型,而不用频繁的去自己写函数重载。
class类模板
类模板是用来生成类的蓝图,与函数模板不同的是,编译器不能为类模板推断模板参数类型。为了使用类模板,我们必须在类名后面加<>中提供额外信息。
template <class T>
class Node
{
public:
void fun()
{
cout << "T* : " << typeid(next).name() << endl;
cout << "T : " << typeid(val).name() << endl;
}
private:
T* next;
T val;
};
int main()
{
Node<int> ni;
ni.fun();
cout << endl;
Node<double> nd;
nd.fun();
cout << endl;
Node<char> nc;
nc.fun();
return 0;
}
运行结果:
此时我们就可以发现,你<>中传什么类型进去,那么你类中的T就是什么类型。这就大大的方便了我们的编码,如果没有模板,那么每个类型都要安插一个这样的类出来,是非常麻烦的。
模板特化
如果你想对不同的模板参数,做不同的处理,那么此时你可以用模板指定类型。比如你有一个正常的模板,但是当模板参数为double时想做一下特殊处理,那么我们可以再定义一个类型模板的类。这样再生成模板时会优先生成最匹配的那一项
//普通模板
template<class T>
class A
{
public:
A()
{
cout << "T()" << endl;
}
};
//特化
template<>
class A<double>
{
public:
A()
{
cout << "double()" << endl;
}
};
int main()
{
A<int> ai;
A<char> ac;
A<double>ad;
return 0;
}
我们来看看它们会调用哪些构造函数。
我们发现,当在其他模板参数类型时会调用T(),但是当模板参数为double时,调用的是double()。 这也就意味着当模板参数为double时,那么会优先选择最匹配的。这种行为被称为特化。
半特化(偏特化)
当你要传入的模板参数,一半需要自动推导,一半又需要自己定义时,可以使用半特化。
template<class T1,class T2>
class A
{
public:
A()
{
cout << "A<T1,T2>" << endl;
}
};
template<class T1>
class A<T1,char>
{
public:
A()
{
cout << "A<T1,char>" << endl;
}
};
int main()
{
A<int,int> a1;
A<double,char> a2;
A<char,char>a3;
return 0;
}
运行结果:
我们发现,只要一个模板参数对应,那么就会优先选择对应的那个。
模板原理
模板的原理其实就是根据你所传的模板参数,又给你自动生成了一个类。而这个过程在编译时发生。就比如如下这个代码。
template<class T1,class T2>
class A
{
public:
T1 a;
T2 b;
};
int main()
{
A<int,int> a1;
A<double,char> a2;
A<char,char>a3;
return 0;
}
它在编译后实际上是这样的
class A
{
public:
int a;
int b;
};
class A
{
public:
double a;
char b;
};
class A
{
public:
char a;
char b;
};
会生成三个A类。分别对应的模板参数
A<int,int> a1;
A<double,char> a2;
A<char,char>a3;
当然,我们在编码的时候是无法用相同的类名的。但在编译时,会有类似于函数重载的机制(个人猜测)生成出多个类。
所以,函数模板的本质就是方便你编码。等编译时再为你自动生成你当初传入的模板参数时对应的类。也就是说,虽然表面上你写的代码减少了,但本质上需要的代码并没有减少。只是在编译时为你自动生成了。