在一个类中使用带有成员函数的通用std :: function对象

时间:2011-09-28 11:30:17

标签: c++ function function-pointers c++11 tr1

对于一个类,我想在一个存储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。

6 个答案:

答案 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消息从一个类转发到另一个类。

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);
    }
};

祝您好运,并感谢大家分享知识。

答案 5 :(得分:0)

您可以避免std::bind这样做:

std::function<void(void)> f = [this]-> {Foo::doSomething();}