Использование общих объектов std:: function с функциями-членами в одном классе
Для одного класса я хочу сохранить некоторые указатели на функции-члены одного и того же класса в одном 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
в сочетании с некоторыми странными ошибками создания шаблона. В настоящее время я работаю над Windows 8 с Visual Studio 2010/2011, а на Win 7 с VS10 тоже не получается. Ошибка должна основываться на некоторых странных правилах С++, которые я не соблюдаю.
EDIT: я использую boost НЕ. Это С++ 11, встроенный в компилятор MS.
Ответы
Ответ 1
Нестатическую функцию-член нужно вызывать с объектом. То есть он всегда неявно передает указатель "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);
Или, если ваш компилятор поддерживает С++ 11 lambdas:
std::function<void(int,int)> f = [=](int a, int b) {
this->doSomethingArgs(a, b);
}
(У меня сейчас нет компилятора С++ 11, поэтому я не могу проверить это.)
Ответ 2
Вам нужно
std::function<void(Foo*)> f = &Foo::doSomething;
чтобы вы могли вызвать его на любом экземпляре, или вам нужно связать конкретный экземпляр, например this
std::function<void(void)> f = std::bind(&Foo::doSomething, this);
Ответ 3
Если вам нужно сохранить функцию-член без экземпляра класса, вы можете сделать что-то вроде этого:
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);
Каким будет тип хранилища без авто? Что-то вроде этого:
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 существует, но с тех пор устарел. Существует предложение, post С++ 17, чтобы сделать указатель на функции-члены вызываемыми. Это было бы очень приятно.
Ответ 4
Вы можете использовать функторы, если вы хотите менее общий и более точный контроль под капотом. Пример с моим Win32 API для пересылки сообщений API из одного класса в другой.
IListener.h
#include <windows.h>
class IListener {
public:
virtual ~IListener() {}
virtual LRESULT operator()(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
};
Listener.h
#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;
}
};
Dispatcher.h
#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);
}
};
Удачи и спасибо всем за обмен знаниями.