头文件:
std::function的实例可以对任何能够调用的目标实体进行封装调用,这些目标实体包括普通函数、lambda表达式、函数指针、仿函数、类的普通成员函数和类的静态成员函数等。std::function对象是对C++中现有的可调用实体的一种类型安全的封装(我们知道像函数指针这类可调用实体是类型不安全的),示例代码如下
#include
#include
using namespace std;
std::function func;
// 普通函数
int testFunc(int a)
{
return a;
}
// lambda表达式
auto lambda = [](int a)->int{ return a; };
// 仿函数(实现operator()的函数)
class Functor
{
public:
int operator()(int a)
{
return a;
}
};
// 类的普通成员函数和静态成员函数
class TestClass
{
public:
int ClassMember(int a) { return a; }
static int StaticMember(int a) { return a; }
};
int main()
{
// 普通函数
func = testFunc;
int result = func(10);
cout << "普通函数:"<< result << endl;
// lambda表达式
func = lambda;
result = func(20);
cout << "lambda表达式:"<< result << endl;
// 仿函数
Functor testFunctor;
func = testFunctor;
result = func(30);
cout << "仿函数:"<< result << endl;
// 类的普通成员函数
TestClass testObj;
func = std::bind(&TestClass::ClassMember, &testObj, std::placeholders::_1);
result = func(40);
cout << "类的普通成员函数:"<< result << endl;
// 类的静态成员函数
func = TestClass::StaticMember;
result = func(50);
cout << "类的静态成员函数:"<< result << endl;
return 0;
}
std::bind是这样一种机制,它可以预先把指定可调用实体的某些参数绑定到已有的变量,产生一个新的可调用实体。可将std::bind函数看作一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。std::bind将可调用对象与其参数一起进行绑定,绑定后的结果可以使用std::function保存。std::bind主要有以下几个作用:
std::bind绑定普通函数
double myDivide(double x, double y)
{
return x/y;
}
auto fn_half = std::bind(myDivide, 1, 2);
std::cout << fn_half() << std::endl; // 0.5
bind的第一个参数是函数名,普通函数做实参时,会隐式转换成函数指针。因此std::bind (myDivide,1,2)等价于std::bind (&myDivide,1,2)
std::bind绑定一个成员函数
struct Foo {
void print_sum(int n1, int n2)
{
std::cout << n1+n2 << '\n';
}
int data = 10;
};
int main()
{
Foo foo;
auto f = std::bind(&Foo::print_sum, &foo, 95, 5);
f(); // 100
}
bind绑定类成员函数时,第一个参数表示对象的成员函数的指针,第二个参数表示对象的地址。必须显示的指定&Foo::print_sum,因为编译器不会将对象的成员函数隐式转换成函数指针,所以必须在Foo::print_sum前添加&。使用对象成员函数的指针时,必须要知道该指针属于哪个对象,因此第二个参数为对象的地址 &foo;
std::bind绑定一个引用参数
默认情况下,bind的那些不是占位符(占位符的概念后面介绍)的参数被拷贝到bind返回的可调用对象中。但是,与lambda类似,有时对有些绑定的参数希望以引用的方式传递,或是要绑定参数的类型无法拷贝。
#include
#include
#include
#include
#include
using namespace std::placeholders;
using namespace std;
ostream & print(ostream &os, const string& s, char c)
{
os << s << c;
return os;
}
int main()
{
vector words{"hello", "world", "this", "is", "C++11"};
ostringstream os;
char c = ' ';
for_each(words.begin(), words.end(),
[&os, c](const string & s){os << s << c;} );
cout << os.str() << endl;
ostringstream os1;
// ostream不能拷贝,若希望传递给bind一个对象,
// 而不拷贝它,就必须使用标准库提供的ref函数
for_each(words.begin(), words.end(),
bind(print, ref(os1), _1, c));
cout << os1.str() << endl;
}
前面的 std::bind 可以绑定一个可调用对象和调用这个可调用对象所需要的参数,比如上面的例子
double myDivide(double x, double y)
{
return x/y;
}
auto fn_half = std::bind(myDivide, 1, 2);
std::cout << fn_half() << std::endl; // 0.5
这段代码中我们把可调用的函数封装在了fn_half中,并把想传入的参数同时传了进去,因此结果是0.5,但如果我们只确定了一个想传入的参数,另一个想在调用std::bind返回的fn_half中使用,那么我们就需要占位符,因此可以概括一下占位符的主要作用可以减少调用参数的数量,也可以将调用参数的位置互换。代码如下
// 减少1个参数
double myDivide(double x, double y)
{
return x/y;
}
auto fn_half = std::bind(myDivide, std::placeholders::_1, 2);
std::cout << fn_half(1) << std::endl; // 0.5
// 减少2个参数
double myDivide(double x, double y)
{
return x/y;
}
auto fn_half = std::bind(myDivide, 1, 2);
std::cout << fn_half() << std::endl; // 0.5
// 参数位置互换
double myDivide(double x, double y)
{
return x/y;
}
auto fn_half = std::bind(myDivide, std::placeholders::_2, std::placeholders::_1);
std::cout << fn_half(1, 2) << std::endl; // 2.0
注意:如果在bind函数中使用了特定数量占位符,那么在调用bind函数返回的可调用对象时,也要有同等数量的参数。