模板函数—使用泛型来定义函数
//example:
template<typename T>
void Swap(T &a,T &b) // 声明或定义
{
T temp;
temp = a;
a = b;
b = temp;
}
int main()
{
int a = 12;
int b = 13;
Swap(a,b);
cout<<a<<" "<< b<<endl;
Swap<int>(a,b);
cout<<a<<" "<< b<<endl;
}
第一行指出建立一个模板类,并将类型命名为T
,关键字template
和typename
是必须的,除非使用关键字class
代替typename
(两个关键字是等价的)
模板函数使用: ------必须确定T的数据类型,才可以使用。
1自动类型推导: Swap(a,b);
-----推导出必须·一致的数据类型
2显示指定类型:Swap<int>(a,b);
普通函数和函数模板的区别:
1普通函数调用可以发生隐式类型转换
2对于函数模板,在自动类型推导下,不可以发生隐式类型转换,但在显示指定类型下可以发生隐式类型转换。
普通函数和模板函数调用规则:
1如果函数模板和普通函数都可以调用,优先调用普通函数
2可以通过空模板参数列表,强制调用函数模板 Swap<>(a,b);
3函数模板可以发生重载
4如果函数模板可以产生更好的匹配,优先调用函数模板
总结:在实际中,提供了模板,就不要在提供普通函数。
模板不是万能的,对于特殊数据类型,需要具体化相应的数据类型的实现。
函数模板显式具体化: template<> void Swap(job &a,job &b); job是类类型,需要给出具体的函数实现
。
函数模板显式实例化:template void Swap(char &a,char &b);就这一句就可以
,编译时就提前生成char类型的函数。
上述两种只能择其一。
//多参数模板
template<typename T1,class T2>
void Print(T1 &a,T2 &b) // 声明或定义
{
cout<<a<<" "<<b<<endl;
}
int main()
{
int a = 12;
double b = 13;
Print(a,b);
Print<int,double>(a,b);
}
struct Node{
int x;
int y;
};
template<typename T>
void Print(T &a) // 声明或定义
{
cout<<a<<" "<<endl;
}
template void Print(int& a);
template<> void Print<Node>(Node &n){//显示具体化
cout<<n.x<<" "<<n.y<<endl;
}
int main()
{
Node node;
node.x =12;
node.y = 2;
Print<Node>(node);
int a = 12;
Print<int>(a);
}
类模板:
如果一个类中的数据成员的数据类型不能确定,或者是某个成员函数的参数或返回值的类型不能确定,就必须将此类声明为模板,它的存在不是代表一个具体的、实际的类,而是代表一类类。
1基础类模板定义:
//template <class nametype, class agetype>//多个
template <class Type>
class className{
private:
Type items[1];
int top;
......;
public:
className();
bool isempty();
..........;
};
template <class Type>
className<Type>::className(){...}
template <class Type>
bool className<Type>::isempty(){...}
上面是类实现,类模板一般将实现和声明放一起。且每一个函数前都要使用template
。
如果一定要类外但同一个文件中实现则需要1声明模板,2声明作用域classname<Type>
如Person类类外实现构造函数: template<class T> Person<T>::Person(T name){...};
如果分文件编写,则在需要该类时,包含头文件为“xxxx.cpp
”直接包含源文件,在源文件中,实现成员函数,同上
对类模板而言,并没有自动类型推导;类模板在模板参数列表可以有默认参数 ----- template <class Type = int>;
则,使用时Person p(2)不会报错;
对类模板而言,成员函数是在调用时创建的,而普通类中的成员函数是一开始创建的。
模板类的继承
在模板类的继承中,需要注意以下几点:
• 如果父类自定义了构造函数,记得子类要使用构造函数列表来初始化(普通的也一样)。
• 继承的时候,如果子类不是模板类,则必须指明当前的父类的类型,因为要分配内存空间
class ChildOne:public Parent<int>{
....
};
• 继承的时候,如果子类是模板类,要么指定父类的类型,要么用子类的泛型来指定父类
template <typename T>
class ChildTwo:public Parent<T>{
....
};
3类模板与友元
全局函数类内声明+类外实例化—直接在类内声明友元即可;在类中:friend void print(Person<T> p){....};
----重要
template<class T>
class Node
{
private:
T x;
T y;
static int all;
public:
Node(T a,T b);
friend void print();//print是所有模板的友元函数,由于没有对象,故只能访问静态变量
friend void print1(Node<T> &);//这里的形参是Node<T>,而不能是Node,因为不存在Node这种类型,只能模板具体化
};
template<class T>
int Node<T>::all = 0;
template<class T>
Node<T>::Node(T a,T b){
all++;
x=a;
y=b;
}
void print(){
cout<<Node<int>::all<<endl;//统计的是int型的个数
}
void print1(Node<int> &n)//模板具体化
{
cout<<n.all<<" "<<n.x<<" "<<n.y<<endl;
}
void print1(Node<double> &n)//模板具体化
{
cout<<n.all<<" "<<n.x<<" "<<n.y<<endl;
}
int main()
{
Node<int> n1(1,1);
print();//1
print1(n1);//1 1 1
Node<int> n2(2,2);
print();//2
print1(n2);//2 2 2
Node<double> n3(1.2,1.2);
print();//2
print1(n3);//1 1.2 1.2
}
全局函数类内声明+类外模板化实现1
template<class T>
void print();
template<class T>
void print1(T &);
template<class T>
class Node
{
private:
T x;
T y;
static int all;
public:
Node(T a,T b);
friend void print<T>();//print是所有模板的友元函数,由于没有对象,故只能访问静态变量
friend void print1<>(Node<T> &);
};
template<class T>
int Node<T>::all = 0;
template<class T>
Node<T>::Node(T a,T b){
all++;
x=a;
y=b;
}
template<class T>
void print(){
cout<<Node<int>::all<<endl;
}
template<class T>
void print1(T &n)
{
cout<<n.all<<" "<<n.x<<" "<<n.y<<endl;
}
int main()
{
Node<int> n1(1,1);
print<int>();
print1(n1);
Node<int> n2(2,2);
print<int>();;
print1(n2);
Node<double> n3(1.2,1.2);
print<int>();
print1(n3);
}
全局函数类内声明+类外模板化实现方法2—需要提前让编译器知道全局函数的存在; ---------尽量别用了
1 template class Person;,在最上方
2类外定义 template void printPerson(Person p){…} ,在1下面,在3上方
3在类中声明:friend void printPerson<>(Person p),
类外定义 template void printPerson(Person p){…}
另外一个常见问题就是在模板函数实现时,有时候可能不能确定某个参数的类型。就用到关键字decltype
template<typename T1,class T2,class T>
//解决1 因为返回值类型无法确定,解决方法1;其实由用户在具体传参时,自己推到该类型,若写的不对,可能会降低精度,或者发生错误。
T add1(T1 a, T2 b) {
decltype(a+b) c = a + b;
return c;
}
//解决2 更好的写法,是返回值类型后置
auto add2(T1 a,T2,b) ->decltype(a+b){
return a+b
}
int main() {
//1
double a = add<double,int,double>(3.4,4);
//2
auto b =add<double,int>(3.3,5);
return 0;
}
long func(int x);
int main(){
int a =10;
const int i = 10;
//1
decltype(a) b;//int
decltype(i) i1;//const int
//2
decltype(func(4)) n;//long 即函数返回值类型,并不会真的执行,只有去查看该函数的返回值类型即可。
//3
decltype((a)) c = b;//int&
//4
decltype(i + 20.3) i2;//double
}