C++11中的function和bind


  C++11中引入lambda,与operator() 和 函数(函数指针)统称为可调用对象。而operator()和lambda也实现了闭包。

闭包的概念

  闭包是带有上下文的函数,这个函数带有状态,也就是重载了()的类。什么叫 “带有状态” 呢? 意思是这个闭包有属于自己的变量,这些个变量的值是创建闭包的时候设置的,并在调用闭包的时候,可以访问这些变量。

  函数是代码,状态是一组变量,将代码和一组变量捆绑,就形成了闭包,内部包含 static 变量的函数不是闭包,因为这个 static 变量不能捆绑。闭包的状态捆绑,必须发生在运行时。

lambda和函数指针

  lambda虽然也叫匿名函数,但是其可以捕捉变量 ,所以和普通函数还是不一样的。

using Func = int(int);
int test(int x) { cout << x << endl; return 0; }
auto lamb = [](int x) ->int { cout << x << endl; return 0; };
void fun(Func f, int x) { f(x); }

int main() {
	fun(test, 5); // 输出5
	fun(lamb, 10);  // 输出10
}

  但是,如果lambda捕捉列表不为空时,则lambda无法转成函数指针。

  那么就引来一个问题,sort函数传入第三个参数时,可以传函数指针、仿函数、lambda。

class Test {
public:
	bool operator()(int& a, int& b) { return a < b; }
};

static bool cmp(int& a, int& b) { return a < b; }
int main() {
	vector<int> arr{ 1, 3, 2, 5, 4, 7, 2 };
	int temp = 5;
	auto lamb = [temp](int& a, int& b) { return a < b; };
	Test t;
	//sort(arr.begin(), arr.end(), cmp);  // 输出1 2 2 3 4 5 7
	//sort(arr.begin(), arr.end(), lamb); // 输出1 2 2 3 4 5 7
	sort(arr.begin(), arr.end(), t);    // 输出1 2 2 3 4 5 7
	for (auto i : arr) {
		cout << i << " ";
	}
}

  从上面可以看出,即使lambda捕捉列表不为空时,传入sort函数的效果依旧和函数指针、仿函数效果一样。

C++11引入了 function来统一它们。

function

  function是一个可调用对象的包装器,是一个类模板,可以容纳除类成员(函数)指针之外的所以可调用对象。它的实例可以对任何可以调用的目标实体进行存储、复制和调用操作。

void test() { cout << 5 << endl; }

class Test {
public:
	void operator()() { cout << 5 << endl; }
};

int main() {
	int x = 5;
	auto lamb = [x]() {cout << x << endl; };
	function<void()> func1{ test };
	function<void()> func2{ Test() };
	function<void()> func3{ lamb };
	func1();  // 输出5
	func2();  // 输出5
	func3();  // 输出5
}

但是不能直接将重载函数的名字存入function类型的对象中:

void test(int x) { cout << x << endl; }
void test(double x) { cout << x << endl; }

int main() {
	function<void(int x)> func{ test }; // error 无法确定需要哪个重载实例
}
// 解决方法,可以通过函数指针和lambda来解决二义性
void test(int x) { cout << x << endl; }
void test(double x) { cout << x << endl; }

void (*Func)(int x) = test;  // 函数指针

int main() {
	function<void(int x)> func1{ Func };
	function<void(int x)> func2{ [](double x) ->void {cout << x << endl; } };
}
bind

  像find_if这个函数,由于其只能接受一个一元谓词,但是需要长度这个信息,所以就引入了lambda表达式。而上面function也提到了,C++11通过function将lambda、函数指针、仿函数统一起来了。所以可不可以用函数指针来实现find_if,这就引入了bind的概念(bind1st和bind2nd已被弃用)

  bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以使用function来进行保存。有两大作用:

  • 将可调用对象与其参数一起绑定成一个仿函数
  • 将多元(参数个数为n,n>1)可调用对象转成一元、二元、…(n-1)元可调用对象,即只绑定部分参数。

  调用bind的一般形式为:

auto newCallable = bind(callable, arg_list);
// 其中,newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应callable的参数。即,
// 当我们调用newCallable时,newCallable会调用callable,并传递给它arg_list中的参数
// arg_list中可能包含形如_n的名字,其中n是一个整数。这些参数是占位符,表示newCallable的参数,他们
// 占据了传递给newCallable的参数位置,数值n表示生成的可调用对象中参数的位置。
// 名字_n都定义在一个名为placeholders的名空间中,而这个名空间又在std中。

  实现find_if。

// 通过lambda实现
int main() {
	vector<string> arr{ "hello", "world!", "C++" };
	auto lamb = [sz = arr.size()](string& s) { return s.size() < 4; };
	auto it = find_if(arr.begin(), arr.end(), lamb);
	if (it != arr.end()) cout << *it << endl;
}

// 通过bind实现
using std::placeholders::_1;

bool cmp(string& s, int size) { return s.size() < 4; }

int main() {
	vector<string> arr{ "hello", "world!", "C++" };
	function<bool(string)> f = bind(cmp, _1, arr.size());
	auto it = find_if(arr.begin(), arr.end(), f);
	if (it != arr.end()) cout << *it << endl;
}
使用bind来替代bind1st和bind2nd
// g++实现bind1st和bind2nd
int main() {
    vector<int> arr{1, 2, 3, 4, 5};
    // 找出比3小的个数
    int count = count_if(arr.begin(), arr.end(), bind1st(greater<int>(),3));
    cout << count << endl;
    // 找出大于等于3的个数
    count = count_if(arr.begin(), arr.end(), bind2nd(greater_equal<int>(), 3));
    cout << count << endl;
}

// 通过bind实现
int main() {
	vector<int> arr{ 1, 2, 3, 4, 5 };
	// 找出小于3的个数
	int count = count_if(arr.begin(), arr.end(), bind(greater<int>(), _1, 3));
	cout << count << endl;
	// 找出大于等于3的个数
	count = count_if(arr.begin(), arr.end(), bind(greater_equal<int>(), 3, _1));
	cout << count << endl;
}
使用组合bind函数

  如何找出集合中大于5小于10的元素个数。

(1)首先,需要一个用来判断是否大于5的功能闭包

bind(greater<int>(), _1, 5);

(2)然后,需要一个判断是否小于10的闭包

bing(greater<int>(), 10, _1);

(3)最后用逻辑与把它们连起来

using std::placeholders::_1;
int main() {
	vector<int> arr{ 1, 2, 7, 8, 9, 10, 12 };
	auto f = bind(logical_and<bool>(), bind(greater<int>(), _1, 5), bind(greater<int>(), 10, _1));
	int count = count_if(arr.begin(), arr.end(), f);
	cout << count << endl;
}
参考资料

(1)《C++Primer》
(2)《快速上手C++11/14》
(3)《深入应用C++11·代码优化与工程级应用》

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值