将 std::function 绑定到不同对象实例的同一函数
Binding a std::function to the same function of a different object instance
是否可以
重新绑定 std::function 以指向相同的函数,但具有不同的对象实例?
假设我有一个对象,该对象具有绑定到另一个函数的 std::function,但如果该对象被复制到另一个实例,我想将 std::function 重新绑定到该新实例而不是旧实例。
#include "stdafx.h"
#include <iostream>
#include <functional>
class EventHandler
{
public:
int Num;
std::function<int()> OnEvent;
EventHandler (int inNum)
{
Num = inNum;
}
EventHandler (const EventHandler& other)
{
Num = other.Num;
OnEvent = other.OnEvent; //TODO: Need some way to redirect the std::function to the new instance rather than having the delegate point to the original object's handler.
}
int HandleEvent ()
{
return Num;
}
};
int main()
{
EventHandler a(4);
a.OnEvent = std::bind(&EventHandler::HandleEvent, a);
EventHandler b(a);
b.Num = 5;
//Uncommenting the line below is a manual way of redirecting event handler to the new instance.
//b.OnEvent = std::bind(&EventHandler::HandleEvent, b);
int aResult = a.OnEvent();
int bResult = b.OnEvent();
//This will print out 4 and 4 instead of 4 and 5 since b is still bound to a's event handler.
std::cout << "aResult=" << aResult << " bResult=" << bResult << 'n';
return 0;
}
我愿意使用标准::函数的包装器来存储其他信息。
下面的代码引入了一个binding_function<R(Args...)>
,它被称为像function<R()>
,参数可以在构造后随时重新绑定(假设它没有被nullptr
)。
#include <functional>
#include <tuple>
#include <utility>
#include <memory>
#include <iostream>
template <typename T>
class binding_function;
template <typename R, typename... Args>
class binding_function<R(Args...)> : std::function<R()>
{
using base_function = std::function<R(Args...)>;
using binded_function = std::function<R()>;
base_function base;
public:
binding_function() = default;
template <typename BaseF, typename... TArgs>
binding_function(BaseF&& f, TArgs&&... args)
: base(std::forward<BaseF>(f)) {
rebind(std::forward<TArgs>(args)...);
}
template <typename... TArgs>
void rebind(TArgs&&... args)
{
static_cast<binded_function&>(*this) =
std::bind(base, std::forward<TArgs>(args)...);
}
using binded_function::operator();
};
class EventHandler
{
public:
// change type of OnEvent to binding_function
binding_function<int(EventHandler)> OnEvent;
// others remain the same
};
int main()
{
EventHandler a(4);
// first binding
a.OnEvent = {&EventHandler::HandleEvent, a};
EventHandler b(a);
b.Num = 5;
b.OnEvent.rebind(b); // rebinding
int aResult = a.OnEvent();
int bResult = b.OnEvent();
//This will print out 4 and 4 instead of 4 and 5 since b is still bound to a's event handler.
std::cout << "aResult=" << aResult << " bResult=" << bResult << 'n';
return 0;
}
事件处理程序的作用应取决于调用它的实例。因此,从逻辑上讲,解决问题的正确方法是将实例作为参数提供给处理程序函数,例如
#include <iostream>
#include <functional>
class EventHandler
{
private:
std::function<int(EventHandler &)> handlingFunction;
public:
int Num;
EventHandler (int inNum)
: handlingFunction ([] (EventHandler &) -> int { throw 0; })
, Num (inNum)
{ }
void SetHandlingFunction (std::function<int(EventHandler &)> f) {
handlingFunction = f;
}
// for convenience, if the handling function is a member
void SetHandlingFunction (int EventHandler::*mf ()) {
handlingFunction =
[mf] (EventHandler & myself) -> int { return myself.*mf (); }
;
}
int OnEvent () {
return handlingFunction (*this);
}
int HandleEvent ()
{
return Num;
}
};
int main()
{
EventHandler a(4);
a.SetHandlingFunction ( [] (EventHandler & h) -> int { return h.HandleEvent (); } );
// or
a.SetHandlingFunction (&EventHandler::HandleEvent);
EventHandler b(a);
b.Num = 5;
int aResult = a.OnEvent();
int bResult = b.OnEvent();
std::cout << "aResult=" << aResult << " bResult=" << bResult << 'n';
return 0;
}
当然,如果您的处理函数始终是成员函数,则可以简单地将std::function
替换为指向成员函数的指针。
请注意,您应该在 EventHandler
类的构造函数中正确初始化 handlingFunction
成员,例如,将其设置为虚拟函数。
我扩展了user1887915的答案以允许带有参数的函数:
#include <functional>
#include <tuple>
#include <utility>
#include <memory>
#include <iostream>
template <typename T>
class binding_function;
template <typename R, typename... Args, typename SelfType>
class binding_function<R(SelfType, Args...)> : std::function<R(Args...)>
{
using base_function = std::function<R(SelfType, Args...)>;
using binded_function = std::function<R(Args...)>;
base_function base;
public:
binding_function() = default;
template <typename BaseF, typename... TArgs>
binding_function(BaseF&& f, SelfType t, TArgs&&... args)
: base(std::forward<BaseF>(f)) {
rebind(std::forward<SelfType>(t), std::forward<TArgs>(args)...);
}
template <typename T, typename... TArgs>
void rebind(T&& t, TArgs&&... args)
{
static_cast<binded_function&>(*this) =
std::bind(base, std::forward<SelfType>(t), std::forward<TArgs>(args)...);
}
using binded_function::operator();
};
class EventHandler
{
public:
int Num;
binding_function<int(EventHandler, int)> OnEvent;
EventHandler (int inNum)
{
Num = inNum;
}
EventHandler (const EventHandler& other)
{
Num = other.Num;
OnEvent = other.OnEvent; //TODO: Need some way to redirect the std::function to the new instance rather than having the delegate point to the original object's handler.
}
int HandleEvent (int value)
{
return Num + value;
}
};
int main()
{
EventHandler a(4);
// first binding
a.OnEvent = {&EventHandler::HandleEvent, a, std::placeholders::_1};
EventHandler b(a);
b.Num = 5;
b.OnEvent.rebind(b, std::placeholders::_1); // rebinding
int aResult = a.OnEvent(1);
int bResult = b.OnEvent(1);
//This will print out 4 and 4 instead of 4 and 5 since b is still bound to a's event handler.
std::cout << "aResult=" << aResult << " bResult=" << bResult << 'n';
return 0;
}
AFAIK 您所要求的是不可能的,但我认为您可以执行一种解决方法:
class EventHandler
{
public:
int Num;
std::function<int()> OnEvent;
template <typename Func>
EventHandler (int inNum, Func on_event)
{
Num = inNum;
OnEvent = [=]() { return (this->*on_event)(); };
}
EventHandler (const EventHandler& other): EventHandler(other.Num, &EventHandler::HandleEvent) {}
int HandleEvent ()
{
return Num;
}
};
int main()
{
EventHandler a(4, &EventHandler::HandleEvent);
EventHandler b(a);
b.Num = 5;
int aResult = a.OnEvent();
int bResult = b.OnEvent();
//This will print out 4 and 4 instead of 4 and 5 since b is still bound to a's event handler.
std::cout << "aResult=" << aResult << " bResult=" << bResult << 'n';
return 0;
}
这将根据需要打印"aResult=4 bResult=5"。另外,我认为通过采用更多的元编程魔法,我们可以尝试美化语法。
让我知道这是否适合您。
相关文章:
- 对象实例化调用构造函数的次数太多
- 为什么C++无法识别我的对象实例化?
- 使用大括号和 : 符号的对象实例化
- 对象实例化与类型C++
- 如何在C++中传递lambda函数内部的对象实例
- 在 c++ 中重置大型对象实例的最佳方法是什么
- 从 QT Widget 对象实例访问 QT Widget 子成员
- C++阻止用户创建对象实例
- cpp 静态对象实例化
- C++:根据变量将同一对象实例化为多种类型之一
- 与 QML 共享C++对象实例
- C 对象实例被销毁后留在列表上
- vulkan vulkan.hpp从对象实例中获取opbject类型
- 用户创建的类对象实例化的未申请标识符
- 对象实例化后不输出
- 限制对象实例化堆
- C C#等价对象实例
- 在C 中运行时从指针访问对象实例
- 使用类/对象实例数据初始化对话框
- 未知的 C++ 对象实例化语法