之前看项目代码在实现表驱动方法时,经常会遇到这样的代码:
// enum { ID0, ID1, ID2 };
// std::map<int, std::function<void()>> Foo::m;
// void Foo::func0(){}
// void Foo::func1(){}
// void Foo::func2(){}
void Foo::init()
{
m[ID0] = std::bind(&Foo::func0, this);
m[ID1] = std::bind(&Foo::func1, this);
m[ID2] = std::bind(&Foo::func2, this);
}
时间长了以后,我一度认为std::bind
的第一个参数是一个非静态类成员函数时,第二个参数只能是相应类对象的指针,因为一直以来都有非静态类成员函数的第一个参数是隐含的this
指针的说法。直到遇到下面的代码:
asio::async_write(socket_, asio::buffer(message_),
boost::bind(&tcp_connection::handle_write, shared_from_this(),
asio::placeholders::error,
asio::placeholders::bytes_transferred));
第二个参数还能是std::shared_ptr
???
打开https://zh.cppreference.com/w/cpp/utility/functional/bind后才发现,第二个参数可接受的类型远比我想象的多,第二个函数不但能接受对象指针,还可以接受对象引用,甚至是另一个std::bind
返回的可调用对象,下面是代码:
#include <iostream>
#include <functional>
class Foo
{
public:
Foo(int key) : m_key(key)
{}
void print()
{
std::cout << "key: " << m_key << std::endl;
}
static Foo create(int key)
{
Foo result(key);
return result;
}
private:
int m_key;
};
template<typename T>
class PtrWrapper
{
public:
PtrWrapper(T *p) : m_p(p)
{}
T &operator*()
{
return *m_p;
}
private:
T *m_p;
};
int main()
{
// 绑定到指针
Foo foo0(0);
auto f0 = std::bind(&Foo::print, &foo0);
f0();
// 绑定到对象
Foo foo1(1);
auto f1 = std::bind(&Foo::print, foo1);
f1();
// 绑定到对象引用
Foo foo2(2);
auto f2 = std::bind(&Foo::print, std::ref(foo2));
f2();
// 绑定到另一个可调用对象
auto f3 = std::bind(&Foo::print, std::bind(&Foo::create, std::placeholders::_1));
f3(3);
// 绑定到placeholder
Foo foo4(4);
auto f4 = std::bind(&Foo::print, std::placeholders::_1);
f4(foo4);
// 绑定到实现了T &operator*()的对象
Foo foo5(5);
PtrWrapper<Foo> pw(&foo5);
auto f5 = std::bind(&Foo::print, pw);
f5();
return 0;
}
我总结出了6类情况,具体类别可见代码中的注释。std::shared_ptr
对应的是最后一种情况。
感觉好麻烦,还是用lambda
表达式吧。