函数模板
template <typename T> //把typename换成class也可以
函数模板调用方法
使用过程中, 有两种方法调用, 如下:
- 自动类型推导:
#include <iostream>template <class T>
void swap(T &first, T &second) {T temp;temp = first;first = second;second = temp;
}int main() {int a = 10;int b = 20;swap(a, b);return 0;
}
- 显示指定类型
#include <iostream>template <class T>
void swap(T &first, T &second) {T temp;temp = first;first = second;second = temp;
}int main() {int a = 10;int b = 20;swap<int>(a, b);return 0;
}
注意, 两者的区别在于调用时是否传入模板类型.
函数模板调用规则
- 普通函数调用时可以发生隐式类型转换
- 函数模板, 用自动类型推导, 不能发生隐式类型转换
- 函数模板, 用显示指定类型, 可以发生隐式类型转换
普通函数和函数模板调用顺序规则
假设有以下例子:
#include <iostream>
using namespace std;void mSwap(int &first, int &second) {cout << "call normal function" << endl;int temp;temp = first;first = second;second = temp;
}template <class T>
void mSwap(T &first, T &second) {cout << "call template function" << endl;T temp;temp = first;first = second;second = temp;
}
- 如果函数模板和普通函数都可以调用, 优先调用普通函数
示例代码:
int main() {int a = 10;int b = 20;mSwap(a, b);return 0;
}
结果:
- 可以通过空模板参数列表强制调用函数模板
示例:
int main() {int a = 10;int b = 20;mSwap<>(a, b);return 0;
}
结果:
- 函数模板可以发生函数重载
比如又加了一个函数:
template <class T>
void mSwap(T &first, T &second) {cout << "call template function" << endl;T temp;temp = first;first = second;second = temp;
}template <class T>
void mSwap(T &first, T &second, T &third) {cout << "call template function" << endl;T temp;temp = first;first = second;second = temp;
}
现在就有两个同名函数模板了, 这样传入三个参数就会调用下面的.
- 如果函数模板可以产生更好的匹配(不需要发生隐式类型转换), 优先调用函数模板
比如有如下调用:
int main() {char a = 'a';char b = 'b';mSwap(a, b);return 0;
}
运行结果为:
函数模板局限性
比如有如下代码:
class People {
public:People(string name, int age) : m_name(name), m_age(age) {}string m_name;int m_age;
};template <class T>
bool mCompare(const T &a, const T &b) {if (a == b)return true;elsereturn false;
}
当有如下调用时:
int main() {People p1("michael", 18);People p2("michael", 18);mCompare(p1, p2);return 0;
}
会发生如下编译错误:
出现以上情况我们当然可以在类中实现" == "的运算符重载, 也可以用如下的方法, 在以上代码的基础上增加以下代码:
template<> bool mCompare(const People &a, const People &b) {if (a.m_name == b.m_name &&a.m_age == b.m_age)return true;elsereturn false;
}
这样即对以上的函数模板进行people定制化重载。
类模板
在类的声明按照如下声明:
template <class nameType, class ageType>
class People {
public:People(nameType name, ageType age) : m_name(name), m_age(age) {}nameType m_name;ageType m_age;
};
这样使用的时候按照如下使用:
int main() {People<string, int> p1("michael", 18);return 0;
}
类模板中成员函数创建时机
类模板中成员函数在调用时才创建.
如下测试代码:
class People1 {
public:void showPerple1(){cout << "this is class People1" << endl;}
};class People2 {
public:void showPerple2(){cout << "this is class People2" << endl;}
};template <class T>
class mClass {
public:void show1() {m_obj.showPerple1();}void show2() {m_obj.showPerple2();}T m_obj;
};
如上代码是可以编译通过的.
如下测试代码:
int main() {mClass <People1> p1;mClass <People2> p2;p1.show1();p2.show2();return 0;
}
编译时候确定了T的类型之后, 如果发现T中没有成员函数中的函数, 就会报错.
类模板的对象作函数参数
假设有如下模板类:
template <class nameType, class ageType>
class People {
public:People(const nameType &name, const ageType &age) : m_name(name), m_age(age) {}void show() {cout << "name: " << m_name << endl;cout << "age: " << m_age << endl;}nameType m_name;ageType m_age;
};
如果想要调用People实例化对象的show函数, 可以有以下方法:
- 指定传入类型
方法如下:
void showPeople1(People<string, int> &p) {p.show();
}int main() {People<string, int> p1("michael", 18);showPeople1(p1);return 0;
}
- 参数模板化
方法如下:
template <class nameType, class ageType>
void showPeople2(People<nameType, ageType> &p) {p.show();
}int main() {People<string, int> p2("michael", 18);showPeople1(p2);return 0;
}
- 整个类模板化
方法如下:
template <class P>
void showPeople3(P &p) {p.show();
}int main() {People<string, int> p3("michael", 18);showPeople3(p3);return 0;
}
类模板继承
- 当父类是模板类时, 子类在继承的时候需要指定父类中的模板类型, 当有如下代码:
template <class T>
class Father {
public:T m_T;
};class Son : public Father {
public:
};
这样编译会报错, 需要改为:
template <class T>
class Father {
public:T m_T;
};class Son : public Father<int> {
public:
};
这样就可以了
- 也可以把子类也变成模板类, 可以按照如下操作:
template <class T>
class Father {
public:T m_T;
};template <class T1, class T2>
class Son : public Father<T2> {
public:T1 m_T1;
};
类外实现
当有如下类:
template <class nameType, class ageType>
class People {
public:People(nameType name, ageType age);void showPeople();nameType m_name;ageType m_age;
};
当我们在类外实现其构造函数和成员函数时, 需要按照如下方式:
//constructor
template <class nameType, class ageType>
People<nameType, ageType>::People(nameType name, ageType age) {m_name = name;m_age = age;
}//member function
template <class nameType, class ageType>
void People<nameType, ageType>::showPeople() {cout << "name: " << m_name << endl;cout << "age: " << m_age << endl;
}
类模板的分文件编写
类模板与友元
函数模板和类模板的区别
- 类模板是不存在类型推导的, 所以实例化对象的时候必须传入类型.
- 类模板是可以有默认参数的
可参考如下代码:
template <class nameType, class ageType = int>
class People {
public:People(nameType name, ageType age) : m_name(name), m_age(age) {}nameType m_name;ageType m_age;
};int main() {People<string> p1("michael", 18);return 0;
}
这样是可以的.
如果两个参数都有默认类型, 实例化对象的时候也必须要有尖括号, 如下:
template <class nameType = string, class ageType = int>
class People {
public:People(nameType name, ageType age) : m_name(name), m_age(age) {}nameType m_name;ageType m_age;
};int main() {People <>p1("michael", 18);return 0;
}
模板的特化
模板的设计是为了有效减少代码量,但是某些情况,对于某种特定类型,模板就需要进行特化。
全特化和偏特化
全特化就是限定死模板实现的具体类型,偏特化就是如果这个模板有多个类型,那么只限定其中的一部分。
函数模板的特化
函数模板只能进行全特化不能进行偏特化,函数模板的全特化可以参照上文说的,正常函数就是相当于对函数模板的全特化。
类模板的特化
template<typename T1, typename T2>
class Test
{
public:Test(T1 i,T2 j):a(i),b(j){cout<<"模板类"<<endl;}
private:T1 a;T2 b;
};template<>
class Test<int , char>
{
public:Test(int i, char j):a(i),b(j){cout<<"全特化"<<endl;}
private:int a;char b;
};template <typename T2>
class Test<char, T2>
{
public:Test(char i, T2 j):a(i),b(j){cout<<"偏特化"<<endl;}
private:char a;T2 b;
};
那么下面3句依次调用类模板、全特化与偏特化:
Test<double , double> t1(0.1,0.2);
Test<int , char> t2(1,'A');
Test<char, bool> t3('A',true);
//依次打印:
//类模板
//全特化
//偏特化