web-dev-qa-db-ja.com

1つのクラスのメンバー関数で汎用std :: functionオブジェクトを使用する

1つのクラスについて、1つの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 argumentsxxcallobjに、奇妙なテンプレートのインスタンス化エラーが組み合わされています。現在、私はWindows 8でVisual Studio 2010/2011を使用していますが、Win 7でVS10を使用している場合も失敗します。エラーは、私が従わないいくつかの奇妙なC++ルールに基づいている必要があります。

編集:NOTブーストを使用します。これは、MSコンパイラに統合されたC++ 11です。

138

非静的メンバー関数は、オブジェクトを使用して呼び出す必要があります。つまり、引数として常に「this」ポインターを暗黙的に渡します。

std::functionシグネチャは、関数が引数(<void(void)>)を受け取らないことを指定しているため、bindが最初の(そして唯一の)引数である必要があります。

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ラムダをサポートしている場合:

std::function<void(int,int)> f = [=](int a, int b) {
    this->doSomethingArgs(a, b);
}

(C++ 11対応のコンパイラは手元にありませんですので、これをチェックすることはできません。)

251
Alex B

どちらかが必要

std::function<void(Foo*)> f = &Foo::doSomething;

そのため、任意のインスタンスで呼び出すことができます。または、thisなどの特定のインスタンスをバインドする必要があります

std::function<void(void)> f = std::bind(&Foo::doSomething, this);
65
Armen Tsirunyan

クラスインスタンスを使用せずにメンバー関数を格納する必要がある場合、次のようなことができます。

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以降、 呼び出し可能なメンバー関数へのポインター にする提案があります。これは大歓迎です。

11
Greg

あまり一般的ではなく、より正確な制御をボンネットの下で行いたい場合は、ファンクターを使用できます。 APIメッセージをクラスから別のクラスに転送するwin32 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);
    }
};

幸運を祈り、知識を共有してくれたすべての人に感謝します。

0
user11158633