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
Using generic std::function objects with member functions in one class
提问by Christian Ivicevic
For one class I want to store some function pointers to member functions of the same class in one map
storing std::function
objects. 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 arguments
in xxcallobj
combined 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 arguments
的xxcallobj
一些奇怪的模板实例化的错误结合。目前我正在使用 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::function
signature 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::doSomething
gives 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::bind
to bind the "pointer to member function" to the this
pointer. The other is to use a lambda that captures the this
pointer 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::function
will result in dynamic memory allocation.
使用 g++ 至少将成员函数绑定到 this 将导致对象大小为三指针,将 this 分配给 anstd::function
将导致动态内存分配。
On the other hand, a lambda that captures this
is only one pointer in size, assigning it to an std::function
will 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::bind
doing this:
您可以避免std::bind
这样做:
std::function<void(void)> f = [this]-> {Foo::doSomething();}