[C++] C++11详解 (五)function包装器、bind绑定

标题:[C++] C++11详解 (五)function包装器、bind

@水墨不写bug



目录

一、function包装器

二、bind绑定


正文开始:

一、function包装器

        function包装器,function实现在<functional>头文件中。C++中的function本质上是一个类模板。

        function包装器可以包装函数指针,仿函数,lambda表达式, 在一定程度上可以起到简化代码逻辑和实现的作用。

//function包装lambda表达式

int x = 10, y = 11;

function<int(int, int)> f1 = [](int a, int b)->int {return a + b; };

cout << f1(x, y) << endl;

//输出21
//function包装函数指针
int add(int a,int b)
{
	return a + b;
}
int main()
{
	function<int(int, int)> f2 = add;

	cout << f2(12, 24) << endl;
	return 0;
}
//输出36
//function包装仿函数
struct ADD
{
	int operator()(int a,int b)
	{
		return a + b;
	}
};
int main()
{
	function<int(int, int)> f3 = ADD();

	cout << f3(1, 19) << endl;
	return 0;
}
//输出20

function语法:

std::function在头文件<functional>

// 类模板原型如下
template <class T> function; // undefined

template <class Ret, class... Args>
class function<Ret(Args...)>;

模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参

        之前我们已经讲解过可变参数模板, 知道了Args代表任意类型,任意个数的参数。在具体使用时,需要为function传递模板参数,格式:

function<返回值类型(形参列表)>

        有了包装器,我们就可以讲返回值和形参列表相同的函数指针,仿函数,lambda表达式看作是同一种类型了。

        比如:初始化一个map<string,function<int(int,int)>>:(函数指针,仿函数,lambda由于被function包装过,所以可以识别为同类型)

#include<functional>
#include<iostream>
#include<map>
using namespace std;

int add(int a,int b)
{
	return a + b;
}
struct ADD
{
	int operator()(int a,int b)
	{
		return a + b;
	}
};
int main()
{
	map<string, function<int(int, int)>> opmap = 
	{ 
		{"函数指针",add},
		{"仿函数",ADD()},
		{"lambda",[](int a, int b)->int {return a + b; }} 
	};
    //函数指针,仿函数,lambda由于被function包装过,所以可以识别为同类型

	return 0;
}

        这样一来,我们只需要使用map的string来使用对应的功能即可,不需要知道内部的具体是什么实现的:

int main()
{
	map<string, function<int(int, int)>> opmap = 
	{ 
		{"函数指针",add},
		{"仿函数",ADD()},
		{"lambda",[](int a, int b)->int {return a + b; }} 
	};//函数指针,仿函数,lambda由于被function包装过,所以可以识别为同类型

	//只要根据string的标识提示使用功能即可,不需要知道具体是 函数指针,仿函数,lambda哪一个实现的。
	cout << opmap["函数指针"](1, 18) << endl;
	cout << opmap["仿函数"](9, 28) << endl;
	cout << opmap["lambda"](1, 23) << endl;

	return 0;
}

       


如果用function包装成员函数,就需要注意一些其他的规则了:

        成员函数有静态成员函数非静态成员函数(普通成员函数),由于有类域的限制,这两种都需要用到域作用限定符。

        function包装普通成员函数时,对普通成员函数取地址需要加上“&”运算符:

struct A
{
	int mu_fuc(int a,int b)
	{
		return a + b;
	}
};
int main()
{
	function<int(int, int)> f = &A::mu_fuc;

	return 0;
}

        但是编译失败,正确的写法是:需要在参数列表前面加上一个类指针:

    function<int(A*,int, int)> f = &A::mu_fuc;

         另外的一种写法也是可以达到相同的效果:

	function<int(A, int, int)> f1 = &A::mu_fuc;

        这两种写法的区别在于调用的时候,传参不同:

第一种,调用时候需要传递对象的地址

function<int(A*,int, int)> f = &A::mu_fuc;
A a;
cout << f(&a, 1, 3) << endl;

第二种,调用的时候需要传递对象

//普通成员函数
function<int(A*,int, int)> f = &A::mu_fuc;
A a;
cout << f(&a, 1, 3) << endl;

function<int(A, int, int)> f1 = &A::mu_fuc;
cout << f1(a, 3, 5) << endl;
cout << f1(A(), 8, 9) << endl;

         已经实例化的有名对象、匿名对象都是可以的。


而对于静态成员函数,可以不加上“&”操作符,但是为了统一记忆,加上“&”比较好。

        正确写法:

	function<int(int, int)> f2 = &A::no_mu_fuc;

二、bind绑定

        std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器)接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表

// 原型如下:
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);

        可以将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。

        调用bind的一般形式:

auto newCallable = bind(callable,arg_list);

        其中,newCallable本身是一个可调用对象(比如:函数指针,仿函数,lambda),arg_list是一个逗号分隔的参数列表,对应给定的callable的参数当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数。

        arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数是“占位符”,表示newCallable的参数,它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对象中参数的位置:_1为newCallable的第一个参数,_2为第二个参数,以此类推。


        其实上面的讲述比较抽象,我们可以从特殊开始理解,从具体的实例来理解bind的作用;


double Div(double a, double b)
{
	if (b == 0)
		throw "div by 0";
	else
		return a / b;
}

//bind:第一个参数位置是一个可调用对象,接下来的两个参数是
//placeholders命名空间内部的唯一标识符_1,_2,_3,...
auto Newdiv = bind(Div, placeholders::_1,placeholders::_2);
	

        在调用的时候,_1代表传给Newdiv函数参数的第一个,_2代表传给Newdiv函数参数的第二个;这样就实现了顺序调整

//_1代表Newdiv调用传参的第一个参数:10
//_2代表Newdiv调用传参的第二个参数:8
auto Newdiv = bind(Div, placeholders::_1,placeholders::_2);

	cout << Newdiv(10, 8);
//最终传递给Div(10,8)

/*--------------------------------------------------------------*/
//_1代表Newdiv调用传参的第一个参数:10
//_2代表Newdiv调用传参的第二个参数:8
auto Newdiv = bind(Div, placeholders::_2, placeholders::_1);

	cout << Newdiv(10, 8);
//最终传递给Div(8,10)

        除了顺序调整,也可以实现参数个数调整;


int func(int a, int b, int c)
{
	return a + b + c;
}
int main()
{
	auto newfunc = bind(func, 100, placeholders::_1, placeholders::_2);
	cout << newfunc(9, 4);

	return 0;
}

        在上述例子中,我们固定第一个参数为100,后面两个参数需要我们自己传递。

当然,调整参数个数和参数顺序也可以混合使用。


完~

未经作者同意禁止转载

  • 24
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水墨不写bug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值