模板的定义与使用
比较简单,使用typename申明(与class关键字等价)
#include <iostream>
template<typename T>
T T_max(const T& a, const T& b) {
return b < a ? a : b;
}
int main() {
int a = 0, b = 2;
auto c = T_max<int>(a,b);
std::cout<<c<<std::endl;
double aa = 0.1, bb = 0.2;
auto cc = T_max<double>(aa,bb);
std::cout<<cc<<std::endl;
std::string sa = "mathematics";
std::string sb = "math";
auto sc = T_max<std::string>(sa,sb);
std::cout<<sc<<std::endl;
}
在编译期,模板会根据使用到的类型参数,生成一个对应参数的函数实体进行编译,这一过程称为:实例化。
模板的编译过程,会首先检查模板代码本身的语法,然后还会检查实例化的代码的有效性。
多模板参数
在有多个模板参数的条件下,因为调用顺序的不同,可能结果也会不同:
#include <iostream>
template<typename T1, typename T2>
T2 func(const T1& a, const T2& b) {
return a + b;
}
int main() {
int a = 1;
double b = 2.3;
auto c = func<int,double>(a,b);
std::cout<<c<<std::endl;
auto d = func<double,int>(b,a);
std::cout<<d<<std::endl;
}
一种解决该问题的办法,就是额外定义一个模板参数:
#include <iostream>
template<typename T1, typename T2, typename RT>
RT func(const T1& a, const T2& b) {
return a + b;
}
int main() {
int a = 1;
double b = 2.3;
auto c = func<int,double,double>(a,b);
std::cout<<c<<std::endl;
}
不过这样,就不能使用C++17中的模板参数自动推导了,必须显示地指定模板参数。
或者,将不能推导的参数,写在前面:
#include <iostream>
template<typename RT,typename T1, typename T2>
RT func(const T1& a, const T2& b) {
return a + b;
}
int main() {
int a = 1;
double b = 2.3;
auto c = func<double>(a,b);
std::cout<<c<<std::endl;
}
C++14 中,可以不显式地声明返回值类型,由编译器自行判断:
#include <iostream>
template<typename T1, typename T2>
auto func(const T1& a, const T2& b) {
return a + b;
}
int main() {
int a = 1;
double b = 2.3;
auto c = func(a,b);
std::cout<<c<<std::endl;
}
如果是C++11,也想使用auto来自行推断,可以使用decltype来捕获函数类型:
#include <iostream>
template<typename T1, typename T2>
auto func(T1 a, T2 b) -> decltype(true?a:b) {
return a + b;
}
int main() {
int a = 1;
double b = 2.3;
auto c = func(a,b);
std::cout<<c<<std::endl;
}
decltype(true?a:b)的返回值会是一个确定的通用的类型,比如这里,会返回一个int&, c的值类别为int&。
但是这种方式可能会导致悬垂引用问题,所以,需要使用std::decay消除引用:
class A {
public:
int data;
explicit A(int data):data(data){}
void add() { ++data; }
// A(const A& lhs) = delete;
// A& operator=(const A& lhs) = delete;
};
bool operator< (const A& lhs, const A& rhs) {
return lhs.data < rhs.data;
}
// template<typename T1, typename T2>
// auto func(T1 a, T2 b) -> decltype(true?a:b){
// return b < a ? a : b;
// }
template<typename T1, typename T2>
auto func(T1 a, T2 b) -> typename std::decay<decltype(true?a:b)>::type {
return b < a ? a : b;
}
int main() {
A a{1};
A& aa = a;
A b{5};
using T = decltype(func(aa,b));
if (std::is_same<T, A&>::value) {
std::cout<<"T is A&"<<std::endl;
} else if(std::is_same<T, A>::value) {
std::cout<<"T is A"<<std::endl;
}
else if(std::is_same<T, A&&>::value) {
std::cout<<"T is A&&"<<std::endl;
}
else {
std::cout<<"T is other"<<std::endl;
}
}
或者使用C++11提供的 typename std::common_type<T1,T2>::type 作为返回值:
template<typename T1, typename T2>
typename std::common_type<T1,T2>::type func(T1 a, T2 b) {
return b < a ? a : b;
}
// C++14 提供了更简便的写法
template<typename T1, typename T2>
std::common_type_t<T1,T2> func(T1 a, T2 b) {
return b < a ? a : b;
}
// 或者直接定义一个返回值类型
// std::common_type<T1,T2> 也可以在这里被使用
template<typename T1, typename T2,
typename RT = std::decay_t<decltype(true?T1():T2())> >
RT func(T1 a, T2 b) {
return b < a ? a : b;
}
函数模板重载
template<typename T> T func (T a, T b) {
return b < a ? a : b;
}
// 优先调用非模板函数
int func(int a, int b) {
std::cout<<"int func()\n";
return b < a ? a : b;
}
int main() {
int a = 10;
int b = 11;
using T = decltype(func(a,b));
auto c = func(a,b);
std::cout<<c<<std::endl;
auto d = func(1.1,2.2);
std::cout<<d<<std::endl;
// 只有非模板函数才会允许强制转换,如果没有int重载,这里会直接编译不过
auto e = func(1.1,'a');
std::cout<<e<<std::endl;
}
常见问题
- 尽量使用值传递,以避免很多复杂问题