概述
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博客