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

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

@水墨不写bug


[C++] C++11详解 (五)function包装器、bind绑定_第1张图片


目录

一、function包装器

二、bind绑定


正文开始:

一、function包装器

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

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

//function包装lambda表达式

int x = 10, y = 11;

function 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 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 f3 = ADD();

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

function语法:

std::function在头文件

// 类模板原型如下
template  function; // undefined

template 
class function;

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

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

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

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

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

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

	return 0;
}

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

int main()
{
	map> 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 f = &A::mu_fuc;

	return 0;
}

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

    function f = &A::mu_fuc;

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

	function f1 = &A::mu_fuc;

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

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

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

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

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

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

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


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

        正确写法:

	function f2 = &A::no_mu_fuc;

二、bind绑定

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

// 原型如下:
template 
/* unspecified */ bind (Fn&& fn, Args&&... args);

// with return type (2)
template 
/* 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,后面两个参数需要我们自己传递。

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


完~

未经作者同意禁止转载

你可能感兴趣的:(Cpp,c++,开发语言)