Verwendung generischer std::function-Objekte mit Mitgliedsfunktionen in einer Klasse

Lesezeit: 7 Minuten

Verwendung generischer stdfunction Objekte mit Mitgliedsfunktionen in einer Klasse
Christian Ivicevic

Für eine Klasse möchte ich einige Funktionszeiger auf Mitgliedsfunktionen derselben Klasse in einer speichern map Speicherung std::function Objekte. Aber ich scheitere gleich am Anfang mit diesem Code:

#include <functional>

class Foo {
    public:
        void doSomething() {}
        void bindFunction() {
            // ERROR
            std::function<void(void)> f = &Foo::doSomething;
        }
};

ich erhalte error C2064: term does not evaluate to a function taking 0 arguments in xxcallobj kombiniert mit einigen seltsamen Fehlern bei der Instanziierung von Vorlagen. Derzeit arbeite ich unter Windows 8 mit Visual Studio 2010/2011 und unter Win 7 mit VS10 schlägt es auch fehl. Der Fehler muss auf einigen seltsamen C++-Regeln beruhen, denen ich nicht folge

Verwendung generischer stdfunction Objekte mit Mitgliedsfunktionen in einer Klasse
Alex B

Eine nicht statische Elementfunktion muss mit einem Objekt aufgerufen werden. Das heißt, es übergibt immer implizit “diesen” Zeiger als Argument.

Weil du std::function Signatur gibt an, dass Ihre Funktion keine Argumente akzeptiert (<void(void)>), müssen Sie binden das erste (und einzige) Argument.

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

Wenn Sie eine Funktion mit Parametern binden möchten, müssen Sie Platzhalter angeben:

using namespace std::placeholders;
std::function<void(int,int)> f = std::bind(&Foo::doSomethingArgs, this, std::placeholders::_1, std::placeholders::_2);

Oder, wenn Ihr Compiler C++11-Lambdas unterstützt:

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

(Ich habe keinen C++11-fähigen Compiler zur Hand im Augenblickalso kann ich das nicht überprüfen.)

  • Da ich nicht auf Boost angewiesen bin, werde ich Lambda-Ausdrücke verwenden;) Trotzdem danke!

    – Christian Ivicevic

    28. September 2011 um 12:01 Uhr

  • @AlexB: Boost.Bind verwendet ADL nicht für die Platzhalter, sondern fügt sie in einen anonymen Namensraum ein.

    – Ildjarn

    28. September 2011 um 16:34 Uhr

  • Ich empfehle, die globale Erfassung zu vermeiden [=] und verwenden [this] um klarer zu machen, was erfasst wird (Scott Meyers – Effective Modern C++ Kapitel 6. Punkt 31 – Vermeidung von Standarderfassungsmodi)

    – Max Raskin

    29. August 2016 um 13:57 Uhr

  • Fügen Sie einfach einen kleinen Tipp hinzu: Der Member-Funktionszeiger kann implizit umgewandelt werden std::functionmit Zusatz this als erster Parameter, wie std::function<void(Foo*, int, int)> = &Foo::doSomethingArgs

    – landerlyoung

    25. November 2019 um 6:44 Uhr

  • @landerlyoung: Fügen Sie den Namen der Funktion oben als “f” hinzu, um die Beispielsyntax zu korrigieren. Wenn Sie den Namen nicht benötigen, können Sie mem_fn(&Foo::doSomethingArgs) verwenden.

    – Val

    23. Mai 2020 um 20:41 Uhr

1647105611 27 Verwendung generischer stdfunction Objekte mit Mitgliedsfunktionen in einer Klasse
Armen Tsirunjan

Entweder Sie brauchen

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

damit Sie es auf jeder Instanz aufrufen können, oder Sie müssen beispielsweise eine bestimmte Instanz binden this

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

  • Vielen Dank für diese großartige Antwort: D Genau das, was ich brauche, konnte ich nicht finden, wie man eine std::function spezialisiert, um eine Member-Funktion für eine beliebige Klasseninstanz aufzurufen.

    – Penelope

    5. April 2018 um 17:03 Uhr

  • Dies wird kompiliert, aber ist es Standard? Sind Sie garantiert, dass das erste Argument ist this?

    – sudo rm -rf Schrägstrich

    9. Juli 2019 um 8:49 Uhr

  • @sudorm-rfslash ja, das bist du

    – Armen Tsirunyan

    9. Juli 2019 um 10:54 Uhr

  • Danke für die Antwort @ArmenTsirunyan … wo im Standard kann ich nach diesen Informationen suchen?

    – sudo rm -rf Schrägstrich

    9. Juli 2019 um 11:00 Uhr

1647105611 103 Verwendung generischer stdfunction Objekte mit Mitgliedsfunktionen in einer Klasse
Greg

Wenn Sie eine Memberfunktion speichern müssen ohne die Klasseninstanz, können Sie so etwas tun:

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

Wie würde die Speicherart ohne aussehen Auto? Etwas wie das:

std::_Mem_fn_wrap<void,void (__cdecl TestA::*)(int),TestA,int> callable

Sie können diese Funktionsspeicherung auch an eine Standard-Funktionsbindung übergeben

std::function<void(int)> binding = std::bind(callable, &testA, std::placeholders::_1);
binding(123); // Call

Vergangene und zukünftige Notizen: Eine ältere Schnittstelle std::mem_func existierte, wurde aber inzwischen verworfen. Es existiert ein Vorschlag, post C++17, zu machen Zeiger auf aufrufbare Elementfunktionen. Dies wäre höchst willkommen.

  • @Danh std::mem_fn war nicht ENTFERNT; ein Haufen unnötiger Überlastungen waren. Andererseits std::mem_fun war mit C++11 veraltet und wird mit C++17 entfernt.

    – Max Truxa

    3. November 2016 um 15:47 Uhr

  • @Danh Genau das meine ich 😉 Die allererste “grundlegende” Überladung ist immer noch da: template<class R, class T> unspecified mem_fn(R T::*);und es geht nicht weg.

    – Max Truxa

    3. November 2016 um 15:56 Uhr

  • @Danh Read der Dr sorgfältig. 12 von 13 Überlastungen wurden von der DR entfernt. Das letzte war nicht (und wird es nicht sein; weder in C++11 noch in C++14).

    – Max Truxa

    3. November 2016 um 16:01 Uhr


  • Warum die Ablehnung? Jede andere Antwort besagte, dass Sie die Klasseninstanz binden müssen. Wenn Sie ein Bindungssystem für Reflexion oder Skripterstellung erstellen, werden Sie dies nicht tun wollen. Diese alternative Methode ist für manche Menschen gültig und relevant.

    – Greg

    3. November 2016 um 23:06 Uhr


  • Danke, Danh, ich habe einige Kommentare zu verwandten vergangenen und zukünftigen Schnittstellen hinzugefügt.

    – Greg

    7. November 2016 um 6:39 Uhr

1647105612 72 Verwendung generischer stdfunction Objekte mit Mitgliedsfunktionen in einer Klasse
Plugwash

Leider erlaubt Ihnen C++ nicht, direkt ein aufrufbares Objekt zu erhalten, das auf ein Objekt und eine seiner Elementfunktionen verweist. &Foo::doSomething gibt Ihnen einen “Zeiger auf die Member-Funktion”, der sich auf die Member-Funktion bezieht, aber nicht das zugehörige Objekt.

Es gibt zwei Möglichkeiten, dies zu umgehen, eine ist die Verwendung std::bind um den “Zeiger auf die Member-Funktion” an die zu binden this Zeiger. Die andere besteht darin, ein Lambda zu verwenden, das die erfasst this Zeiger und ruft die Member-Funktion auf.

std::function<void(void)> f = std::bind(&Foo::doSomething, this);
std::function<void(void)> g = [this](){doSomething();};

Letzteres wäre mir lieber.

Mit g++ führt zumindest das Binden einer Mitgliedsfunktion an dieses zu einem Objekt mit einer Größe von drei Zeigern, das einem zugewiesen wird std::function führt zu dynamischer Speicherzuweisung.

Auf der anderen Seite ein Lambda, das einfängt this nur einen Zeiger groß ist, weist man ihn einem zu std::function führt nicht zu einer dynamischen Speicherzuweisung mit g++.

Obwohl ich dies nicht mit anderen Compilern überprüft habe, vermute ich, dass dort ähnliche Ergebnisse gefunden werden.

Sie können vermeiden std::bind Dies tun:

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

Sie können Funktoren verwenden, wenn Sie eine weniger generische und präzisere Steuerung unter der Haube wünschen. Beispiel mit meiner Win32-API, um eine API-Nachricht von einer Klasse an eine andere Klasse weiterzuleiten.

IListener.h

#include <windows.h>
class IListener { 
    public:
    virtual ~IListener() {}
    virtual LRESULT operator()(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
};

Zuhörer.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);
        }

};

Nutzungsprinzipien

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

Viel Glück und danke an alle für den Wissensaustausch.

994540cookie-checkVerwendung generischer std::function-Objekte mit Mitgliedsfunktionen in einer Klasse

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy