对于一个类,我想在一个存储map
对象的std::function
中存储一些函数指针到同一个类的成员函数。但是我在开头用这段代码就失败了:
class Foo {
public:
void doSomething() {}
void bindFunction() {
// ERROR
std::function<void(void)> f = &Foo::doSomething;
}
};
我在error C2064: term does not evaluate to a function taking 0 arguments
中收到了xxcallobj
,并结合了一些奇怪的模板实例化错误。目前我正在使用Visual Studio 2010/2011在Windows 8上工作,在使用VS10的Win 7上工作也失败了。错误必须基于我不遵循的一些奇怪的C ++规则。
编辑:我不使用提升。这是在MS编译器中集成的C ++ 11。
答案 0 :(得分:239)
必须使用对象调用非静态成员函数。也就是说,它总是隐式地将“this”指针作为其参数传递。
因为您的std::function
签名指定您的函数不接受任何参数(<void(void)>
),所以您必须绑定第一个(也是唯一的)参数。
std::function<void(void)> f = std::bind(&Foo::doSomething, this);
如果要使用参数绑定函数,则需要指定占位符:
using namespace std::placeholders;
std::function<void(int,int)> f = std::bind(&Foo::doSomethingArgs, this, _1, _2);
或者,如果您的编译器支持C ++ 11 lambdas:
std::function<void(int,int)> f = [=](int a, int b) {
this->doSomethingArgs(a, b);
}
(我现在没有一个支持C ++ 11的编译器,,所以我无法检查这个。)
答案 1 :(得分:64)
要么
std::function<void(Foo*)> f = &Foo::doSomething;
以便您可以在任何实例上调用它,或者您需要绑定特定实例,例如this
std::function<void(void)> f = std::bind(&Foo::doSomething, this);
答案 2 :(得分:11)
如果你需要存储一个成员函数而没有类实例,你可以这样做:
class MyClass
{
public:
void MemberFunc(int value)
{
//do something
}
};
// Store member function binding
auto callable = std::mem_fn(&MyClass::MemberFunc);
// Call with late supplied 'this'
MyClass myInst;
callable(&myInst, 123);
没有 auto ,存储类型会是什么样子? 像这样:
std::_Mem_fn_wrap<void,void (__cdecl TestA::*)(int),TestA,int> callable
您也可以将此功能存储传递给标准功能绑定
std::function<void(int)> binding = std::bind(callable, &testA, std::placeholders::_1);
binding(123); // Call
过去和将来的注释:旧的界面 std :: mem_func 已存在,但此后已被弃用。在C ++ 17之后,提出了一个提案pointer to member functions callable。这将是最受欢迎的。
答案 3 :(得分:2)
不幸的是,c ++不允许您直接获取引用一个对象及其成员函数之一的可调用对象。 “&Foo :: doSomething”为您提供了“指向成员函数的指针”,该指针指向成员函数,但不是关联的对象。
有两种解决方法,一种是使用std :: bind将“指向成员函数的指针”绑定到this指针。另一种方法是使用lambda捕获此指针并调用成员函数。
std::function<void(void)> f = std::bind(&Foo::doSomething, this);
std::function<void(void)> g = [this](){doSomething();};
我希望稍后。
使用g ++至少将一个成员函数绑定到此函数,将导致对象的大小为三个指针,将其分配给std :: function将导致动态内存分配。
另一方面,捕获该lambda的大小只有一个指针,将其分配给std :: function不会导致g ++动态分配内存。
虽然我尚未与其他编译器对此进行过验证,但我怀疑在那里会发现类似的结果。
答案 4 :(得分:0)
如果要在引擎盖下进行通用性和更精确的控制,可以使用仿函数。我的win32 api的示例将api消息从一个类转发到另一个类。
#include <windows.h>
class IListener {
public:
virtual ~IListener() {}
virtual LRESULT operator()(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
};
#include "IListener.h"
template <typename D> class Listener : public IListener {
public:
typedef LRESULT (D::*WMFuncPtr)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
private:
D* _instance;
WMFuncPtr _wmFuncPtr;
public:
virtual ~Listener() {}
virtual LRESULT operator()(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) override {
return (_instance->*_wmFuncPtr)(hWnd, uMsg, wParam, lParam);
}
Listener(D* instance, WMFuncPtr wmFuncPtr) {
_instance = instance;
_wmFuncPtr = wmFuncPtr;
}
};
#include <map>
#include "Listener.h"
class Dispatcher {
private:
//Storage map for message/pointers
std::map<UINT /*WM_MESSAGE*/, IListener*> _listeners;
public:
virtual ~Dispatcher() { //clear the map }
//Return a previously registered callable funtion pointer for uMsg.
IListener* get(UINT uMsg) {
typename std::map<UINT, IListener*>::iterator itEvt;
if((itEvt = _listeners.find(uMsg)) == _listeners.end()) {
return NULL;
}
return itEvt->second;
}
//Set a member function to receive message.
//Example Button->add<MyClass>(WM_COMMAND, this, &MyClass::myfunc);
template <typename D> void add(UINT uMsg, D* instance, typename Listener<D>::WMFuncPtr wmFuncPtr) {
_listeners[uMsg] = new Listener<D>(instance, wmFuncPtr);
}
};
class Button {
public:
Dispatcher _dispatcher;
//button window forward all received message to a listener
LRESULT onMessage(HWND hWnd, UINT uMsg, WPARAM w, LPARAM l) {
//to return a precise message like WM_CREATE, you have just
//search it in the map.
return _dispatcher[uMsg](hWnd, uMsg, w, l);
}
};
class Myclass {
Button _button;
//the listener for Button messages
LRESULT button_listener(HWND hWnd, UINT uMsg, WPARAM w, LPARAM l) {
return 0;
}
//Register the listener for Button messages
void initialize() {
//now all message received from button are forwarded to button_listener function
_button._dispatcher.add(WM_CREATE, this, &Myclass::button_listener);
}
};
祝您好运,并感谢大家分享知识。
答案 5 :(得分:0)
您可以避免std::bind
这样做:
std::function<void(void)> f = [this]-> {Foo::doSomething();}