之前看項目代碼在實現表驅動方法時,經常會遇到這樣的代碼:
// 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
表達式吧。