参数绑定bind、bind1st、bind2nd以及ptr_fun、mem_fun、mem_fun_ref的使用

概述

std::bind函数定义在头文件functional中,是一个函数模板,可以将bind函数看作一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来"适应"源对象的参数列表。同时,使用std::bind函数还可以实现参数顺序调整等操作。

函数原型

头文件:functional

调用形式:auto newCallable = bind(callable, arg_list);

1.newCallable:新可调用对象
2.callable:原可调用对象
3.arg_list:是一个逗号分隔的参数列表,它要么是被绑定的值,要么是占位符(_1,_2),对应给定的callable的参数,要求其数量与callable的参数数量相同,顺序和对应的值也要和callable匹配
当我们调用newCallable时,newCallable会调用callable,并传递给它arg_list中的参数,arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数是“占位符”表示newCallable的参数,它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对象中参数的位置_1为newCallable的第一个参数,_2为第二个参数,以此类推,_n也就是调用时实参的位置,在调用时会将实参根据占位符进行赋值替换_n定义在placeholders的命名空间中,所以使用的之前需要使用这个命名空间:using namespace std::placeholders;

标准库bind函数使用场景

将函数转换为泛型算法适用的参数
例如用在find_if调用中的lambda比较一个string和一个给定大小,我们可以很容易地编写一个完成同样工作的函数

bool check_size(const string& s, string::size_type sz)
{
	return s.size() >= sz;
}

但是,我们不能用这个函数作为find_if的一个参数,因为find_if接受一个一元谓词,因此传递给find_if的可调用对象必须接受单一参数,为了可以用check_size来作为参数,就要使用到bind函数。

绑定check_size的sz参数

使用bind生成一个调用check_size的对象,用一个定值作为其大小参数来调用check_size。

auto check6 = bind(check_size, _1, 6);

check6是一个可调用对象,接受一个string类型的参数,并用此string和值6来调用check_size,此bind调用只有一个占位符,表示check6只接受单一参数,这样,使用bind我们可以将原来基于lambda的find_if调用替换成使用check_size的版本,此bind调用生成一个可调用对象,将check_size的第二个参数绑定到sz的值。

int main()
{
	vector<string> words;
	words.push_back("a");
	words.push_back("aa");
	words.push_back("aaa");
	words.push_back("aaaa");
	words.push_back("aaaaa");

	int sz = 3;
	auto check6 = bind(check_size, _1, sz);

	auto iter = find_if(words.begin(), words.end(), [sz](const string& a) {return a.size() >= sz; });
	auto iter2 = find_if(words.begin(), words.end(), check6);
	if (iter != words.end())
		cout << *iter << endl;
	if (iter != words.end())
		cout << *iter2 << endl;
}

bind参数

bind参数中占位符的顺序可以任意设置,在调用时,参数会与之对应。

	//g是一个有俩个参数的可调用对象
	auto g = bind(f, a, b, _2, c, _1);

调用g(X,Y)会调用  f(a,b,Y,c,X)

当bind的参数中占位符和原可调用对象的参数一一对应时,它所产生的新可调用对象和原可调用对象的作用相同,也可以将bind的参数进行重排,来完成相反的作用效果

bool isShorter(const string& s1, const string& s2)
{
    return s1.size() > s2.size();
}

int main()
{
    //按单词长度由短至长排序
	sort(words.begin(), words.end(), isShorter);
    sort(words.begin(), words.end(), bind(isShorter, _1, _2));  //和上行代码作用相同

	//按单词长度由长至短排序
	sort(words.begin(), words.end(), bind(isShorter, _2, _1));
}

第一个调用中,当sort需要比较俩个元素A和B时,它会调用isShorter(A,B),它和第二个调用方法一样,在第三个对sort的调用中,传递给isShorter的参数被交换过来了,因此当sort比较俩个元素时,就好像调用isShorter(B,A)一样

bind类成员函数

作为类成员函数,需要注意的一点是,如果是非静态的成员函数,它会存在一个默认的this指针,静态的成员函数则不存在this指针,所以在将其作为bind函数的参数时,需要注意使用this指针作为其中一个参数,当使用静态成员函数作为参数时,其用法和全局函数类似,当参数为类内非静态成员函数时,第一个参数必须使用&符号

(以下代码会和《函数到指针的隐式转换,非静态成员函数的特殊》文章有联系,可以先查看该文章来理解)

#include "pch.h"
#include <iostream>
#include <functional>
using namespace std::placeholders;
using namespace std;

void fff(int* p)
{
	cout << "fff" << endl;
}

class B
{
public:
	void SetValue(function<void(int*)>op);

};

void B::SetValue(function<void(int *)>op)
{
	int* p = nullptr;
	op(p);
}

class A
{
public:
	void func(int* p);
	void fun();
	static void func1(int* p);
};

void A::func1(int* p)
{
	cout << "A::func1" << endl;
}

void A::func(int* p)
{
	cout << "A::func" << endl;
}

void A::fun()
{
	B b;
	b.SetValue(bind(&fff, _1));				//fff
	b.SetValue(bind(fff, _1));				//fff
	b.SetValue(bind(*fff, _1));				//fff
	b.SetValue(bind(*****fff, _1));			//fff
	b.SetValue(fff);						//fff
	b.SetValue(&fff);						//fff
	b.SetValue(*fff);						//fff
	b.SetValue(*****fff);					//fff

	cout << "--------------------" << endl;

	b.SetValue(bind(&A::func1, _1));		//func1
	b.SetValue(bind(A::func1, _1));			//func1
	b.SetValue(bind(func1, _1));			//func1
	b.SetValue(bind(*func1, _1));			//func1
	b.SetValue(bind(*****func1, _1));		//func1
	b.SetValue(A::func1);					//func1
	b.SetValue(&A::func1);					//func1
	b.SetValue(*A::func1);					//func1
	b.SetValue(*****A::func1);				//func1
	
	cout << "--------------------" << endl;

	A a;
	b.SetValue(bind(&A::func, &a, _1));		//func
	b.SetValue(bind(&A::func, this, _1));   //func
	//b.SetValue(bind(A::func, this, _1));  //非静态的成员函数不加&符号会报错,因为他不能默认转换为函数指针
}

int main()
{
	A a;
	a.fun();
}

ptr_fun

作用:将非成员函数转化为函数对象,只能转化参数数量为1个或者2个的函数

定义:
Arg Arg1 Arg2:函数的参数类型
Result:函数的返回类型

template <class Arg, class Result>
pointer_to_unary_function<Arg, Result> ptr_fun(Result(*f)(Arg))
{
	return pointer_to_unary_function<Arg, Result>(f);
}

template <class Arg1, class Arg2, class Result>
pointer_to_binary_function<Arg1, Arg2, Result> ptr_fun(Result(*f)(Arg1, Arg2))
{
	return pointer_to_binary_function<Arg1, Arg2, Result>(f);
}

使用:
模板的参数列表可以简化不用写,但ptr_fun的参数必须要有,而且需要满足ptr_fun的适用范围。

void fun(int i)
{
	cout << i << endl;
}

void foo(int i, int i2)
{
	cout << i << "---" << i2 << endl;
}

void func(int i, int i2, int i3)
{
	cout << i << i2 << i3 << endl;
}

int main()
{
	auto a = ptr_fun(fun);
	auto a1 = ptr_fun<int, void>(fun);
	a(10);  //10
	a1(10); //10

	auto b1 = ptr_fun(foo);
	auto b2 = ptr_fun<int, int, void>(foo);
	b1(10, 10); //10---10
	b2(10, 10); //10---10

	//错误,ptr_fun只能转化一元函数和二元函数
 	//auto c1 = ptr_fun(func);
}

mem_fun(指针版:通过类对象的指针,也就是this指针来调用)

作用:将成员函数转换为函数对象,只能转换参数数量为1个或者2个的成员函数,需要注意的是成员函数本身自带一个默认参数,就是this指针,当调用的时候这个参数不能缺少

定义:

Arg:成员函数的参数
_Result:成员函数的返回值
_Ty:成员函数所在的类

template<class _Result,class _Ty>
_NODISCARD inline mem_fun_t<_Result, _Ty> mem_fun(_Result(_Ty::*_Pm)())
{	// return a mem_fun_t functor adapter
	return (mem_fun_t<_Result, _Ty>(_Pm));
}

template<class _Result,class _Ty,class _Arg>
_NODISCARD inline mem_fun1_t<_Result, _Ty, _Arg> mem_fun(_Result(_Ty::*_Pm)(_Arg))
{	// return a mem_fun1_t functor adapter
	return (mem_fun1_t<_Result, _Ty, _Arg>(_Pm));
}

template<class _Result,	class _Ty>
_NODISCARD inline const_mem_fun_t<_Result, _Ty> mem_fun(_Result(_Ty::*_Pm)() const)
{	// return a const_mem_fun_t functor adapter
	return (const_mem_fun_t<_Result, _Ty>(_Pm));
}

template<class _Result,	class _Ty,class _Arg>
_NODISCARD inline const_mem_fun1_t<_Result, _Ty, _Arg> mem_fun(_Result(_Ty::*_Pm)(_Arg) const)
{	// return a const_mem_fun1_t functor adapter
	return (const_mem_fun1_t<_Result, _Ty, _Arg>(_Pm));
}

使用:
模板的参数列表可以简化不用写,但men_fun的参数必须要有,而且需要满足mem_fun的适用范围。

mem_fun_ref(引用版:通过类对象的引用来调用)

作用:和mem_fun的作用相同

使用:模板的参数列表可以简化不用写,但men_fun_ref的参数必须要有,而且需要满足mem_fun_ref的适用范围。

class A
{
public:
	void fun()
	{
		cout << "A::fun" << endl;
	}
	void foo(int i)
	{
		cout << "A::foo" << i << endl;
	}
	void func(int i, int i2)
	{
		cout << "A::func" << i << i2 << endl;
	}
};

int main()
{
	A a;
	A* pa = new A();

	function<void(A*)> f1 = mem_fun(&A::fun);
	f1(&a);			//A::fun
	f1(pa);			//A::fun

	function<void(A*, int)> f2 = mem_fun(&A::foo);
	f2(&a, 100);	//A::foo100
	f2(pa, 100);	//A::foo100

	auto f3 = mem_fun<void, A>(&A::fun);
	f3(&a);			//A::fun
	f3(pa);			//A::fun

	function<void(A*, int)> f4 = mem_fun<void, A, int>(&A::foo);
	f4(&a, 100);	//A::foo100
	f4(pa, 100);	//A::foo100
	//auto f5 = mem_fun(&A::func);   错误,mem_fun不能转换参数过多的成员函数

	//通过传入类对象进行调用
	auto f5 = mem_fun_ref(&A::fun);
	f5(a);			//A::fun
	f5(*pa);		//A::fun

	function<void(A&, int)> f6 = mem_fun_ref<void, A, int>(&A::foo);
	f6(a, 100);		//A::foo100
	f6(*pa, 100);	//A::foo100
}

注意:mem_fun可能会与带默认值的形参产生关系,移植平台代码可能会造成影响,且men_fun所绑定的可调用对象不能是标准库中的(目前不了解其含义,先记录)

bind1st与bind2nd

作用:将二元函数转换为一元函数

bind1st:将可调用对象的第一个参数绑定为某值
bind2nd:将可调用对象的第二个参数绑定为某值

参数:
参数1:可调用对象
参数2:将要绑定的值

bind1st、bind2nd与bind函数的区别

1.绑定的可调用对象的参数数量:bind1st、bind2nd函数只能绑定参数数量为2的可调用对象,而bind可以绑定无参数的、以及1~9个参数的可调用对象
2.绑定的可调用对象是否需要借助模板的转换:bind1st、bind2nd函数需要借助ptr_fun、mem_fun、mem_fun_ref等函数对可调用对象进行转换,而bind函数不需要借助这些模板,bind函数包含这些模板的功能
3.实参赋值绑定的参数:bind1st、bind2nd会将其中一个参数绑定,然后另一个参数通过调用时实参进行赋值,而bind函数主要看绑定时的参数顺序以及所用的占位符

void fun(int i)
{
	cout << i << endl;
}

void foo(int i, int i2)
{
	cout << i << "---" << i2 << endl;
}

void func(int i, int i2, int i3)
{
	cout << i << i2 << i3 << endl;
}

class A
{
public:
	void fun()
	{
		cout << "A::fun" << endl;
	}
	void foo(int i)
	{
		cout << "A::foo" << i << endl;
	}
	void func(int i, int i2)
	{
		cout << "A::func" << i << i2 << endl;
	}
};

int main()
{
	A a;
	A* pa = new A();

	function<void(int)> function1 = bind1st(ptr_fun(foo), 10);
	function<void(int)> function2 = bind2nd(ptr_fun(foo), 10);
	function1(20);		//10---20  将10绑定到参数1上,调用时传入20为参数2赋值
	function2(20);		//20---10  将10绑定到参数2上,调用时传入20为参数1赋值
	function<void(int)> function3 = bind(foo, _1, 10);
	function<void(int,int)> function4 = bind(foo, _2, 10);
	function<void(int, int)> function5 = bind(foo, _2, _2);
	function3(20);		//20---10  _1对参数1进行占位,10赋值给参数2,调用时将20为参数1赋值
	function4(20,20);	//20---10  _2对参数1进行占位,10赋值给参数2,调用时虽然传入了2个参数,但是只有占位的参数才会被实参赋值替换,也就是20只会赋值给参数1
	function5(10, 20);	//20---20  bind将原可调用对象的俩个参数使用_2进行占位,调用时即用实参2进行赋值

	auto function6 = bind1st(mem_fun(&A::foo), &a);
	auto function7 = bind1st(mem_fun(&A::foo), pa);
	function6(20);		//A::foo20
	function7(20);		//A::foo20
	auto function8 = bind(&A::foo, _1, 20);
	auto function9 = bind(&A::foo, _2, 20);
	function8(pa);		//A::foo20  
	function9(pa, pa);  //A::foo20  此时参数1并没有什么作用

	vector<int> v{ 20,3,40,5,60,7,8,9 };

	auto fun1 = bind(less<int>(), _1, 10);  //小于10的
	auto fun2 = bind(less<int>(), 10, _2);  
	auto fun3 = bind1st(less<int>(), 10);	//大于10的
	auto fun4 = bind2nd(less<int>(), 10);	//小于10的
	cout << "----------------------" << endl;
	auto iter1 = find_if(v.begin(), v.end(), fun1);
	cout << *iter1 << endl;					//3
	//auto iter2 = find_if(v.begin(), v.end(), fun2);  //fun2为俩个参数的可调用对象,不可以使用
	//cout << *iter2 << endl;
	auto iter3 = find_if(v.begin(), v.end(), fun3);
	cout << *iter3 << endl;					//20
	auto iter4 = find_if(v.begin(), v.end(), fun4);
	cout << *iter4 << endl;					//3
}

绑定引用参数(ref、cref)

默认情况下,bind的那些不是占位符的参数被拷贝到bind返回的可调用对象中,与lambda类似,有时对有些绑定的参数我们希望以引用方式传递,或是要绑定参数的类型无法拷贝,则要用到ref函数。

为了替换一个引用凡是捕获的ostream的lambda:

	//os是一个局部变量,引用一个输出流
	//c是一个局部变量,类型为char
	for_each(words.begin(), words.end(), [&os, c](const string& s) {os << s << c; });

其他相关文章:【精选】stl 函数适配器之std::mem_fun(c++98),sd::mem_fun_ref(C++98), std::men_fn(C++11)的用法-CSDN博客
巧妙使用std::mem_fun-CSDN博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值