C++11实用语法3(function包装器,bind)
1.包装器
function包装器:
function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。那么我们来看看,我们为什么需要function呢?
当我们看见下面这个表达式时,我们可能会猜想func可能是什么呢?比如func是函数名?函数指针?还是函数对象(仿函数对象),还是lambda表达式对象呢?这些都有可能,因此我们把以上的类型统一叫作可调用类型。有如此丰富的类型,可能会导致模板效率低下,有没有什么办法让他们都变成一个类型呢?这就是我们要提到的function包装器啦!!!
ret = func(x);
我们先来看一下下面这段代码:
我们定义了一个普通函数,一个仿函数。还有哟个lambda表达式,将他们分别传入到useF函数中,由于三种函数的类型不同所以useF会实例化出三种不同的函数来分别调用相应的函数。这就导致了模板的效率低下,而包装器可以很好的解决上面的问题。
double f(double i)//普通函数
{
return i / 2;
}
struct Functor //仿函数
{
double operator()(double d)
{
return d / 3;
}
};
template<class F,class T> //函数模板
T useF(F f, T t)
{
static int count = 0;
cout << "count:" << ++count << endl;
cout << "count:" << &count << endl;
return f(t);
}
int main()
{
cout << useF(f, 3) << endl;
cout << useF(Functor(), 3) << endl;
cout << useF([](double e)->double { return e / 3; }, 3) << endl;
}

在使用包装器之前需要包含他的头文件<functional>
// 类模板原型如下
template <class T> function; // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
//模板参数说明:
//Ret: 被调用函数的返回类型
//Args…:被调用函数的形参
接下来我们用function包装器来修改一下原来的代码
因为function是一个类模板,因此使用时需要主动实列化,但是他的传参有些奇怪需要记住,需要包含返回值,和形参个数和类型,并且将参数用()包起来,而返回值直接放在参数的()前面即可,不需要加逗号分隔。
包装器就是用来接收可调用对象的,因此只需要与可调用函数的参数和返回值相同的类型来实列化function包装器就可以接收相应的可调用函数,这时我们再通过包装器来调用函数,此时我们发现这时的模板只实列化了一份,count的地址也是完全相同的
double f(double i)//普通函数
{
return i / 2;
}
struct Functor //仿函数
{
double operator()(double d)
{
return d / 3;
}
};
template<class F,class T> //函数模板
T useF(F f, T t)
{
static int count = 0;
cout << "count:" << ++count << endl;
cout << "count:" << &count << endl;
return f(t);
}
int main()
{
function<double(double)> func1 = f;
function<double(double)> func2 = Functor();
function<double(double)> func3 = [](double e)->double {return e / 3; };
cout << useF(func1, 6) << endl;
cout << useF(func2, 6) << endl;
cout << useF(func3, 6) << endl;
return 0;
}

接下来我们来看看function包装器如何来包装成员函数。
当类里面有静态成员函数是,我们可以向普通函数一样对其进行包装,只需要指定类域即可,至于&可以加也可以不加,但是对于普通的成员函数,就必须指定类域并且加上&,同时在function的<>中的()中需要加上类名用来指定类域.
class Plus
{
public:
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return a + b;
}
}
int main()
{
function<int(int, int)> func4 = &Plus::plusi;
cout << func4(1, 2) << endl;
function<double(Plus, double, double)> func5 = &Plus::plusd;
cout << func5(Plus(), 1.1, 2.2) << endl
}
2.bind函数
std::bind函数定义在<functional>头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可
调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。一般而
言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M
可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺
序调整等操作。
// 原型如下:
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2)
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
接下来我们来看一下他的使用方法:
我们用auto来推导类型,他的本质和lambda表达相似,都是再内部实现了仿函数重载operator();
这里的func1 表示了绑定函数Plus,参数分别调用 func1 的第一,二个参数指定。placeholders是一个命名空间,里面包含了_1._2....代表被绑定函数的第几个参数。这里表示Plus函数的第一和第二个参数。 所以func1中的 3 传给了_1, 5 传给了_2; _1则传回给Plus函数的a, _2则传回Plus函数的b;
#include <functional>
int Plus(int a, int b)
{
return a + b;
}
int main()
{
auto func1 = bind(Plus,placeholders::_1,placeholders::_2);
cout<<func1(3,5)<<endl;
}
但是如果我们将placeholders::_1 和 placeholders::_2 反过来会发生什么呢?
这个时候func1传入的 3 会传给_2 , 5 会传给 _1 ,这与上面对比可以发现,绑定后的func1函数的传值是按顺序的_2在前,前面的值就传给_2, 但是被绑定的函数Puls却是按照占位符的顺序来的,_2的值会传给第二个形参 b , _1则传给第一个参数a ;这样就是先的参数的调换。
#include <functional>
int Plus(int a, int b)
{
return a + b;
}
int main()
{
auto func1 = bind(Plus,placeholders::_2,placeholders::_1);
cout<<func1(3,5)<<endl;
}
我们来看看怎么对类是成员函数进行绑定
对静态成员函数进行绑定时,只需要指定类域,加上占位符就好了,而对普通成员函数进行绑定的时候,需要指定类域加上&,因为在类中需要this指针所以还需要传入一个类的对象,匿名对象也可以。
class Plus
{
public:
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return a + b;
}
}
int main()
{
Plus s;
std::function<int(int, int)> func1 = std::bind(&Plus::plusi,
placeholders::_1, placeholders::_2);
std::function<int(int, int)> func2 = std::bind(&Plus::plusd, s ,
placeholders::_1, placeholders::_2);
cout << func1(1, 2) << endl;
cout << func2(1, 2) << endl;
}
更多推荐

所有评论(0)