【C++11 新特性】bind(二)

一、C++11为什么要引入std::bind?

C++11 引入了std::bind,顾名思义,是用来绑定函数调用的某些参数的。std::bind能改造现有函数,生成新的函数。举例说明,现在有这么个函数声明:

int f(int a, int b);

我现在需求,我要一个有 2 个 int 类型参数的函数,并且第二个参数默认为 2。你可千万不要屁颠屁颠的在去写一个f(int i, int i = 2),这里std::bind的作用体现出来了,看:

std::bind(&f, std::placeholders::_1, 2);

好了,std::bind之后的返回值,那就可以拜托给我们的std::funtion同学了,我们定义个函数类型:

std::function<int(int,int)> fun = std::bind(&f, std::placeholders::_1, 2);

std::bind的返回值给 fun,于是我们就生成了一个 b 强制为 2,只有 1 个int a参数的 fun 新函数。

最后要说一句,std::bind返回后的函数和原函数是 2 个完全不同的函数,这个你可以通过打印他们的内存地址看出来,这个就交给各位看官自己去实现了。

二、概述

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

使用std::bind可以将可调用对象和参数一起绑定,绑定后的结果使用std::function进行保存,并延迟调用到任何我们需要的时候,所以经常用来实现回调函数

std::bind通常有两大作用:

  • 将可调用对象与参数一起绑定为另一个std::function供调用
  • 将 n 元可调用对象转成 m(m < n) 元可调用对象,绑定一部分参数,这里需要使用std::placeholders

三、函数原型

std::bind函数有两种函数原型,定义如下:

template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);

template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);

1. 函数模板说明

  • 基于参数fn返回一个函数对象,并且以args参数绑定为函数对象的参数。
  • 每个参数要么绑定一个参数值,要么绑定为一个std::placeholders(占位符,如 _1, _2, ..., _n)。
  • 如果参数绑定成一个值,那么返回的函数对象将总使用绑定的参数值做为调用参数,即调用传入参数将不起作用;如果参数绑定为std::placeholders,那么返回的函数对象在被调用时需要传入实时参数,参数填充的位置即由placeholder指定的序号。

2. 函数模板参数说明

  • fn:可调用的函数对象,比如函数对象、函数指针、函数引用、成员函数或者数据成员函数。
  • args:需要绑定的函数的参数列表,使用命名空间占位符std::placeholders::_1std::placeholders::_2标志参数,其中std::placeholders::_1标志为参数列表中的第一个参数,std::placeholders::_2标志参数列表中的第二个参数,std::placeholders::_3标志参数列表中的第三个参数,以此类推。

3. 函数模板返回值说明

返回一个函数对象,该函数在调用时使用参数列表args来调用fn。如果fn是指向类的成员函数,则返回函数第一个参数应该是该类的成员、或者成员对象的引用、或者是成员对象的指针。

四、示例

#include <iostream>
#include <functional>

// 普通函数
int testFun(int a, int b) {
	return a + b;
}

// Lambda表达式
auto lamdaExps = [](int a, int b) {
	return a + b;
};

// 仿函数
class Functor {
public:
	int operator()(int a, int b) {
		return a + b;
	}
};

// 1.类成员函数
// 2.类静态成员函数
class TestClass
{
public:
	int classMemberFun(int a, int b) { return a + b; }
	static int staticMemberFun(int a, int b) { return a + b; }
};

int main() {
	// 绑定普通函数(使用占位符先占位,然后执行时再传参)
	auto bindFun = std::bind(&testFun, std::placeholders::_1, std::placeholders::_2);
	int ret = bindFun(10, 5);
	std::cout << "普通函数_1:" << ret << std::endl;
	// 绑定普通函数(也可以只使用一个占位符来占位,指定一个参数)
	auto bindFun2 = std::bind(&testFun, std::placeholders::_1, 8);
	ret = bindFun2(10);
	std::cout << "普通函数_2:" << ret << std::endl;

	// 绑定Lambda表达式
	auto bindFun3 = std::bind(lamdaExps, std::placeholders::_1, std::placeholders::_2);
	ret = bindFun3(10, 20);
	std::cout << "Lambda表达式:" << ret << std::endl;

	// 绑定仿函数
	Functor testFunctor;
	auto bindFun4 = std::bind(testFunctor, std::placeholders::_1, std::placeholders::_2);
	ret = bindFun3(10, 30);
	std::cout << "仿函数:" << ret << std::endl;

	// 绑定类成员函数
	TestClass testObj;
	auto bindFun5 = std::bind(&TestClass::classMemberFun, testObj, std::placeholders::_1, std::placeholders::_2);
	ret = bindFun5(10, 40);
	std::cout << "类成员函数:" << ret << std::endl;
	// 绑定类静态成员函数
	auto bindFun6 = std::bind(&TestClass::staticMemberFun, std::placeholders::_1, std::placeholders::_2);
	ret = bindFun6(10, 50);
	std::cout << "类静态成员函数:" << ret << std::endl;

	return 0;
}

结果如下:

普通函数_1:15
普通函数_2:18
Lambda表达式:30
仿函数:40
类成员函数:50
类静态成员函数:60

关于绑定模块函数、模板类的示例可以参考:C++11 - std::bind简要介绍以及可绑定函数的几种形式总结


参考:

浅谈std::function和std::bind

C++11中的std::bind


原文地址:https://www.cnblogs.com/linuxAndMcu/p/14575979.html