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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7582546/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 17:10:58  来源:igfitidea点击:

Using generic std::function objects with member functions in one class

c++functionfunction-pointersc++11tr1

提问by Christian Ivicevic

For one class I want to store some function pointers to member functions of the same class in one mapstoring std::functionobjects. But I fail right at the beginning with this code:

对于一个类,我想在一个map存储std::function对象中存储一些指向同一类成员函数的函数指针。但是我一开始就失败了,这段代码是:

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

I receive error C2064: term does not evaluate to a function taking 0 argumentsin xxcallobjcombined with some weird template instantiation errors. Currently I am working on Windows 8 with Visual Studio 2010/2011 and on Win 7 with VS10 it fails too. The error must be based on some weird C++ rules i do not follow

我收到error C2064: term does not evaluate to a function taking 0 argumentsxxcallobj一些奇怪的模板实例化的错误结合。目前我正在使用 Visual Studio 2010/2011 在 Windows 8 上工作,在使用 VS10 的 Win 7 上它也失败了。该错误必须基于我不遵循的一些奇怪的 C++ 规则

回答by Alex B

A non-static member function must be called with an object. That is, it always implicitly passes "this" pointer as its argument.

必须使用对象调用非静态成员函数。也就是说,它总是隐式地传递“this”指针作为它的参数。

Because your std::functionsignature specifies that your function doesn't take any arguments (<void(void)>), you must bindthe first (and the only) argument.

因为您的std::function签名指定您的函数不接受任何参数 ( <void(void)>),所以您必须绑定第一个(也是唯一的)参数。

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

If you want to bind a function with parameters, you need to specify placeholders:

如果要绑定带参数的函数,则需要指定占位符:

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

Or, if your compiler supports C++11 lambdas:

或者,如果您的编译器支持 C++11 lambdas:

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

(I don't have a C++11 capable compiler at hand right now, so I can't check this one.)

(我没有一个C ++在手部11能够编译器现在,所以我不能检查这一项。)

回答by Armen Tsirunyan

Either you need

要么你需要

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

so that you can call it on any instance, or you need to bind a specific instance, for example this

这样你就可以在任何实例上调用它,或者你需要绑定一个特定的实例,例如 this

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

回答by Greg

If you need to store a member function withoutthe class instance, you can do something like this:

如果您需要在没有类实例的情况下存储成员函数,您可以执行以下操作:

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

What would the storage type look like without auto? Something like this:

如果没有auto,存储类型会是什么样子?像这样的东西:

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

You can also pass this function storage to a standard function binding

您还可以将此函数存储传递给标准函数绑定

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

Past and future notes: An older interface std::mem_funcexisted, but has since been deprecated. A proposal exists, post C++17, to make pointer to member functions callable. This would be most welcome.

过去和未来的笔记:旧接口std::mem_func存在,但已被弃用。在 C++17 之后,存在一个提议,使指向成员函数的指针可调用。这将是最受欢迎的。

回答by plugwash

Unfortunately, C++ does not allow you to directly get a callable object referring to an object and one of its member functions. &Foo::doSomethinggives you a "pointer to member function" which refers to the member function but notthe associated object.

不幸的是,C++ 不允许您直接获取引用对象及其成员函数之一的可调用对象。&Foo::doSomething给你一个“指向成员函数的指针”,它指的是成员函数而不是关联的对象。

There are two ways around this, one is to use std::bindto bind the "pointer to member function" to the thispointer. The other is to use a lambda that captures the thispointer and calls the member function.

有两种方法可以解决这个问题,一种是使用std::bind将“指向成员函数的this指针”绑定到指针。另一种是使用一个 lambda 来捕获this指针并调用成员函数。

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

I would prefer the latter.

我更喜欢后者。

With g++ at least binding a member function to this will result in an object three-pointers in size, assigning this to an std::functionwill result in dynamic memory allocation.

使用 g++ 至少将成员函数绑定到 this 将导致对象大小为三指针,将 this 分配给 anstd::function将导致动态内存分配。

On the other hand, a lambda that captures thisis only one pointer in size, assigning it to an std::functionwill not result in dynamic memory allocation with g++.

另一方面,捕获的 lambdathis大小只有一个指针,将其分配给 anstd::function不会导致使用 g++ 进行动态内存分配。

While I have not verified this with other compilers, I suspect similar results will be found there.

虽然我没有用其他编译器验证过这一点,但我怀疑会在那里找到类似的结果。

回答by plugwash

You can use functors if you want a less generic and more precise control under the hood. Example with my win32 api to forward api message from a class to another class.

如果你想要一个不那么通用和更精确的控制,你可以使用函子。使用我的 win32 api 将 api 消息从一个类转发到另一个类的示例。

IListener.h

监听器.h

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

Listener.h

监听器.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

调度员.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);
        }

};

Usage principles

使用原则

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

Good luck and thank to all for sharing knowledge.

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

回答by aggsol

You can avoid std::binddoing this:

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

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