16.1.1 定义函数模板
使用template+模板参数列表来定义一个函数模板。
template<typename T>
bool compare(const T&a,const T&b){
return a<b;
}
compare(1,1);
在具体调用时,会根据传入的实参类型,将函数模板中的模板参数替换掉,转换为一个具体的函数实例。 这个过程叫做实例化。函数模板可以根据传入的实参类型推断出模板参数类型,前提是所有的模板参数类型都用到,我们也可以自己指定参数类型。
compare<int,int>(1,1);
我们可以使用typename和class声明模板参数,但是一般使用typename,因为这样更加的清晰,class是旧版本遗留下来的。
使用typename和class声明的参数模板是一个类型,但是这个类型具体是什么要在根据传入的实参类型来确定。我们还可以当以非类型模板参数,非类型模板参数它本身就已经是一个具体的类型的变量了,这个值由用户提供或者编译器推断。
下面的调用方式就是由编译器自己推断得出的,用来获取数组的大小。
template<typename value_type, unsigned arr_size>
constexpr unsigned get_arr_size(value_type(&arr)[arr_size]) {
return arr_size;
}
int a[] = {1,2,3,4,54,56};
int b[get_arr_size(a)];
下面这种就是用户自己指定的
template<typename T,unsigned vlaue>
void print(T v) {
cout<<v<<endl;
}
print<int, 1>(233);
模板函数一样的可以被inline和constexpr修饰。
模板的声明和定义都放在一块且放在头文件中,而不是像普通的类一样,声明和定义分别在头文件和源文件中
函数模板的很多错误都是在实例化的阶段报告。
练习
16.1
实例化就是为模板创建一个具体的例子,在函数模板中体现是函数模板根据传入的实参类型将模板参数变为实参的类型生成一个具体的函数。
16.2
如果传入一个int和一个double类型的变量则会报错,因为函数模板无法实例化出一个形参列表为int,double的compare
template <typename T>
bool compare(const T& a, const T&b) {
return a < b;
}
cout<<compare(1, 2)<<endl;
cout<<compare(2.2, 2.2)<<endl;
16.3
编译报错,提示Sales_date没有重载<运算符
cout<< compare(data1, data2)<<endl;
16.4
template <typename Iter_type,typename Value_Type>
Iter_type my_find(const Iter_type& begin,const Iter_type& end,Value_Type target_value) {
Iter_type target_iter = end;
for (Iter_type iter = begin;iter!=end;++iter)
{
if (*iter==target_value)
{
target_iter = iter;
break;
}
}
return target_iter;
}
16.5
template<typename value_type,unsigned arr_size>
void print(value_type (&arr)[arr_size]) {
for (auto item :arr)
{
cout << item << endl;
}
}
int a[] = {1,2,3,4,54,56};
double b[] = {2,3,4,54,654};
string c[] = {"12312","2222","6666"};
print(a);
print(b);
print(c);
16.6
标准库对数组的begin和end,很可能也是使用模板函数,对于begin则返回首元素地址,对于end则返回首元素地址+元素个数
template<typename value_type, unsigned arr_size>
value_type* my_end(value_type(&arr)[arr_size]) {
return arr + arr_size;
}
template<typename value_type, unsigned arr_size>
value_type* my_begin(value_type (&arr)[arr_size]) {
return arr;
}
int a[] = {1,2,3,4,54,56};
for (auto iter = my_begin(a); iter !=my_end(a);++iter)
{
cout << *iter << endl;
}
16.7
template<typename value_type, unsigned arr_size>
constexpr unsigned get_arr_size(value_type(&arr)[arr_size]) {
return arr_size;
}
//因为创建数组需要输入常量表达式,为了验证
//返回的是常量表达式所以使用get_arr_size()的实例
//创建一个数组
int a[] = {1,2,3,4,54,56};
int b[get_arr_size(a)];
auto value = get_arr_size(a);
cout << value << endl;
16.8
截图来自C++ Primer第97页,一个直接的原因是对迭代器使用!=在所有的标准库容器上都是可以使用的,但是<符号则不一定适用,有些容器没有定义<,比如关联式容器,list等等,因此为了更加的让代码更加通用在迭代器和指针的遍历中使用!=更好。