可变参数模板
C++11的新特性可变参数模板能够让我们创建可以接受可变参数的函数模板和类模板
// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args... args,这个擦拭农户包中可以包含0到任意个模板参数
template<class ...Args>
void show(Args... args){}
上面的参数args
前面有省略号,所以它就是一个可变模板参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N个模板参数。我们无法直接获取参数包args中的每个参数,只能通过展开参数包的方式来获取参数包中的每个参数。
递归函数方式展开参数包
#include <iostream>
using namespace std;
// 递归终止函数
template<class T>
void showlist(T t)
{
cout << t << endl; // 当参数包只剩下最后一个参数时,会适配这个函数
}
// 递归函数
template <class T, class ...Args>
void showlist(T val, Args... args)
{
cout << val << endl;
showlist(args...); // 通过递归调用,函数会自动适配参数个数,此处的三个...不能省略
}
int main()
{
showlist(1);
showlist(1, "fff");
showlist(3, 1.1, string("hello"));
return 0;
}
函数包装器
function包装器也叫做适配器,C++中的function本质是一个类模板,也是一个包装器
我们都知道一个函数
func
有可能是一个函数,有可能是一个函数指针,有可能是一个仿函数,也有可能是一个lambda表达式。这些都是可调用的类型,如此丰富的类型,可能会导致模板的效率低下。q
举个例子
#include <iostream>
using namespace std;
template <class T, class F>
void func(T t, F f)
{
static int a = 0; // 如果模板只实例化一次,每个a的地址是一样的
cout << &a << endl;
}
int main()
{
func(1, 1);
func(1, 1.1);
func('1', 2);
return 0;
}
可以发现通过函数模板去实例化的函数是三分不同的函数。
// 通过函数包装器使用函数
#include <functional>
#include <iostream>
#include <functional>
template <class T, class F>
void fun(T t, F f)
{
static T x;
x = t;
std::cout << typeid(t).name() << std::endl;
}
class functest
{
public:
int add(int x, int y)
{
return x + y;
}
static int mul(int x, int y)
{
return x * y;
}
};
int main()
{
functest ft;
// 使用包装器,包装成员函数,需要加上类类型
std::function<int(functest,int, int)> f1 = &functest::add;
std::cout << f1(functest(),1, 2) << std::endl;
// 使用包装器,包装静态成员函数,不需要加上类类型
std::function<int(int, int)> f2 = &functest::mul;
std::cout << f2(2, 3) << std::endl;
// 使用包装器包装lambda表达式
double x = 1.2;
std::function<double(double)> f3 = [&](double y)->double { return x *= y; };
std::cout << f3(4) << std::endl;
return 0;
}
bind函数
std::bind
函数定义在头文件中,是一个函数模板,他像一个函数包装器(适配器),接受一个可调用对象,生成一个新的可调用对象来“适应”对象的参数列表。一般而言,我们用它可以把一个原本接收N个参数的函数,通过绑定一些参数,返回一个接收M个(M可以大于N)参数的新函数。同时,使用std::bind
函数还可以实现参数顺序调整等操作。
// bind函数的原型
template <class Fn, class ...Args>
bind(Fn&&, Args&& ...args);
可以将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表,使用方法如下:
auto newCallable = bind(callable, arg_list);
举个例子
// bind函数使用举例
#include<iostream>
#include<functional>
class functest
{
public:
int add(int x, int y)
{
return x + y;
}
};
double mutl(double x, double y)
{
return x * y;
}
int main()
{
// 将mutl函数的第一个参数固定为1,后面在调用函数的时候只需要传一个参数
std::function<double(double)> fun1 = std::bind(mutl, 1 , placeholders::_1);
std::cout << fun1(2.1) << std::endl;
// 使用bind函数绑定成员变量
std::function<int(int, int)> fun2 = std::bind(&functest::add, functest(), placeholders::_1, placeholders::_2);
std::cout << fun2(1, 2) << std::endl;
return 0;
}