C++:template(函数模板)
基础使用
在使用函数的过程中可能会遇到同一个函数名被多次调用的情况,比如
#include <iostream>using namespace std;void Swap(int &i1,int &i2){int tmp;tmp = i1;i1 = i2;i2 = tmp;cout <<"交换后的整形1="<<i1<<"交换后的整形2="<<i2<<endl;
}
void Swap(double &d1,double &d2){double tmp;tmp = d1;d1 = d2;d2 = tmp;cout <<"交换后的浮点形1="<<d1<<"交换后的浮点形2="<<d2<<endl;
}
void Swap(string &s1,string &s2){string tmp;tmp = s1;s1 = s2;s2 = tmp;cout <<"交换后的字符串1="<<s1<<"交换后的字符串2="<<s2<<endl;
}
int main(){int a1 = 3, a2 = 4;double d1 = 3.5, d2 = 4.5;string s1 = "绿茶", s2 = "清补凉";Swap(a1,a2);Swap(d1,d2);Swap(s1,s2);
}
输出结果
交换后的整形1=4交换后的整形2=3
交换后的浮点形1=4.5交换后的浮点形2=3.5
交换后的字符串1=清补凉交换后的字符串2=绿茶
这样定义多个函数虽然也能实现想要的效果,但使用函数模板更加方便
#include <iostream>using namespace std;template<typename T>
void Swap(T &t1,T &t2){T tmp;tmp = t1;t1 = t2;t2 = tmp;cout <<"交换后的整形/浮点形/字符串1="<<t1<<"交换后的整形/浮点形/字符串2="<<t2<<endl;
}
int main(){int a1 = 3, a2 = 4;double d1 = 3.5, d2 = 4.5;string s1 = "绿茶", s2 = "清补凉";Swap(a1,a2);Swap(d1,d2);Swap(s1,s2);
}
输出结果
交换后的整形/浮点形/字符串1=4交换后的整形/浮点形/字符串2=3
交换后的整形/浮点形/字符串1=4.5交换后的整形/浮点形/字符串2=3.5
交换后的整形/浮点形/字符串1=清补凉交换后的整形/浮点形/字符串2=绿茶
这样大大节约了写代码的时间
假如传进去的数据类型不一样,则定义的函数模版需要两个typename
#include <iostream>using namespace std;template<typename T1, typename T2>
void Swap(T1 &t1,T2 &t2){cout <<t1<<"的价格是" << t2 << endl;
}
int main(){int a1 = 3, a2 = 4;double d1 = 3.5, d2 = 4.5;string s1 = "绿茶", s2 = "清补凉";Swap(s1,a1);Swap(s2,a2);
}
函数模板还能为函数指定返回值,但要注意返回值要相同
#include <iostream>using namespace std;template<typename T1, typename T2>
T1 Swap(T1& t1, T2& t2) {cout << t1 << "的价格是" << t2 << endl;return t1;
}
int main(){int a1 = 3, a2 = 4;double d1 = 3.5, d2 = 4.5;string s1 = "绿茶", s2 = "清补凉";string ss1 = Swap(s1,a1);string ss2 = Swap(s2,a2);cout << ss1 << ss2 << endl;
}
输出结果
绿茶的价格是3
清补凉的价格是4
绿茶清补凉
函数模板的具体化
定义:可以提供一个具体化的函数定义,当编译器找到与函数调用匹配的具体化定义时,将使用该定义,不再寻找模板
假如传入类的对象可以这么写
#include <iostream>using namespace std;
class C {
public:string s;C(const string& ss) :s(ss) {}
};template<typename T1, typename T2>void Swap(T1& t1, T2& t2) {string c3 = t1.s;t1.s = t2.s;t2.s = c3;
}
int main(){C c1("绿茶");C c2("清补凉");cout << "交换前c1是" << c1.s << "c2是" << c2.s << endl;Swap(c1,c2);cout << "交换后c1是" << c1.s << "c2是" << c2.s << endl;
}
输出结果
交换前c1是绿茶c2是清补凉
交换后c1是清补凉c2是绿茶
但是当Swap要实现既能传入string也能传入类的对象时,需要具体化函数模板
#include <iostream>using namespace std;
class C {
public:string s;C(const string& ss) :s(ss) {}
};template<typename T1, typename T2>
void Swap(T1& t1, T2& t2) {string tmp = t1;t1 = t2;t2 = tmp;
}
template<>
void Swap<C>(C& t1, C& t2)//用尖括号指定的数据类型,不加其实也可以
{
string c3 = t1.s;
t1.s = t2.s;
t2.s = c3;
}
int main() {C c1("绿茶");C c2("清补凉");string s1 = "绿茶";string s2 = "清补凉";cout << "交换前c1是" << c1.s << "c2是" << c2.s << endl;cout << "交换前s1是" << c1.s << "s2是" << c2.s << endl;Swap(c1, c2);Swap(s1, s2);cout << "交换后c1是" << c1.s << "c2是" << c2.s << endl;cout << "交换后s1是" << c1.s << "s2是" << c2.s << endl;
}
输出结果
交换前c1是绿茶c2是清补凉
交换前s1是绿茶s2是清补凉
交换后c1是清补凉c2是绿茶
交换后s1是清补凉s2是绿茶
分文件编写
普通函数和函数模板的具体化版本在头文件中声明,在源文件中定义,创建函数模版的声明和定义都在头文件中,否则会运行出错
注意
1、虚函数不能用,析构函数也不能用
2 、使用函数模板不会发生隐式转换,除了显式指定函数模板的数据类型时会
3、可以重载
4、数据类型必须对应
5、优先级:具体函数>函数模板具象化>函数模板,如果在定义函数的同时还想使用函数模板或者具体化的函数模板可以<>,比如Swap<>(a,b);
6、如果函数模板的参数更加匹配,将优先使用