C++模板编程与泛型编程之函数模板
缺省参数
//普通函数
int mf(int tmp1, int tmp2)
{
return 1;
}
int mf2(int tmp1, int tmp2)
{
return 10;
}
//函数指针类型定义
typedef int(*FunType)(int, int);
template <typename T,typename F = FunType>
void testfunc(T i, T j, F funcpoint = mf)
{
cout << funcpoint(i, j) << endl;
}
- 对类型F设置默认类型为FunType ,对变量设置为函数指针为mf
- 进行调用,没有指定第三个参数,则使用默认值mf。
testfunc(15, 16);
testfunc(15, 16, mf2);
template <typename T = int, typename U>
void testfunc2(U m)
{
T tmpvalue = m;
cout << tmpvalue << endl;
}
testfunc2(12);
- 输入12,推断出U为int,T默认为int,编译通过。类模板不能这样指定缺省参数,凡是一个指定,后面类型参数都必须全部指定缺省参数。
非类型模板参数
- 基本概念
- 前面的函数模板涉及到的 模板参数都是 “类型 模板参数”需要用typename(class)来修饰(类型参数化)。
- 模板参数还可以是 “非类型 模板参数(普通的参数)”,只是这个普通参数放到<>内
template < typename T, typename U ,int val = 100>
auto Add(T tv1,U tv2)
{
return tv1 + tv2 + val;
}
cout << Add<float, float>(22.3f, 11.8f) << endl;
cout << Add<float, float,800>(22.3f, 11.8f) << endl;
- 调用时没有指定第三个参数,则使用默认值100,也可以自己指定参数,如800。
- c++17开始,支持非类型模板参数为auto类型。
template < typename T, typename U ,auto val = 100>
auto Add(T tv1,U tv2)
{
return tv1 + tv2 + val;
}
- 指定非类型模板参数的值时,一般给的都是常量。因为编译器在编译的时候就要能够确定非类型模板参数的值。
int k = 1000;
cout << Add<float, float, k>(22.3f, 11.8f) << endl;
- 编译器在编译的时候不知道 k 为1000,只有在运行的时候才能确定其值。编译器编译时必须确定知道 k 的值,所以上述代码编译会报错。
- 并不是任何类型的参数都可以作为非类型模板参数。
- int类型可以,但double,float或者类类型string等等可能就不可以,不过double *这种指针类型可以。
- 下面编译时会报 “ val 类型非法 ”的错误。
template < typename T, typename U ,double val = 12.3>
auto Add(T tv1,U tv2)
{
return tv1 + tv2 + val;
}
template <double *p>
void mft()
{
cout << "mft()执行了" << endl;
}
double g_d = 1.3;
mft<&g_d>();
- 总结;
- 一般允许做非类型模板参数的类型如下:(可能不断增加)
- 整型或者枚举型;
- 指针类型;
- 左值 引用类型;
- auto或者decltype(auto);
- 可能还有其他类型,遇到自行总结。
比较奇怪的语法
- 不管类型还是非类型模板参数,如果代码中没有用到,则参数名可以省略。
//template <typename T,int value>
template <typename, int>
auto Add2()
{
return 100;
}
cout << Add2<int,45>() << endl;
- 如 T 和 value在后面代码中没有用到,可以省略不写,语法上不报错。
- 类型前面可以增加一个typename修饰以明确标识一个类型
template <typename T, typename int value>
auto Add2()
{
return 100;
}
- 第一个typename修饰的是类型模板参数 T ,typename可以用class替换
- 第二个typename仅仅表示修饰后面的类型,我们知道后面就是一个类型,为int,此处有画蛇添足之嫌。
- 但是有些场合,当编译器不能区分出来后面是不是类型的时候(我们知道是一个类型),编译器会报错,此时就可以加上typename告诉编译器后面是一个类型。
- 一般跟模板有关的类型名前面是需要typename