【问题标题】:C++11: How to access targets of std::functions with different types?C++11:如何访问不同类型的 std::functions 目标?
【发布时间】:2012-05-16 08:07:46
【问题描述】:

我想编写一个最小的多播委托类。它的接口由三个用于调用委托的运算符 operator() 和用于添加/删除可调用类型(如函数、方法、lambda 或仿函数)的 operator+=/operator-= 组成。

我在实现 operator-= 时遇到了麻烦,我需要比较函数目标的地址以进行删除。到目前为止,这是我想出的(我使用 g++ 4.6.3):

#include <functional>
#include <list>

template <typename ... EventArgs>
class Event
{
public:
    typedef std::function<void (EventArgs...)> EventHandler;

    Event() = default;
    ~Event() = default;
    Event(const Event &) = delete;
    Event & operator=(const Event &) = delete;

    void operator()(EventArgs... eventArgs)
    {
        for (auto eventHandler : m_eventHandlers)
            eventHandler(eventArgs...);
    }

    Event & operator+=(const EventHandler &eventHandler)
    {
        m_eventHandlers.push_back(eventHandler);
        return *this;
    }

    Event & operator-=(const EventHandler &eventHandler)
    {
        m_eventHandlers.remove_if([&](const EventHandler &_eventHandler)
        {
            return false; // TODO: Compare event handlers
        });

        return *this;
    }

private:
    std::list<EventHandler> m_eventHandlers;
};
当我用以下代码替换 TODO 行时,

operator-= 适用于函数:

return * eventHandler.template target<void (*)(EventArgs...)>() ==
       *_eventHandler.template target<void (*)(EventArgs...)>();

这是一个最小的例子:

void eventHandler() { /* ... */ }

Event<> event;
event += eventHandler;
event();
event -= eventHandler; // works

但是,对于其他可调用类型,它会崩溃,因为 std::function.target() 返回空指针。显然模板不再适合,所以我尝试模板 Event::operator-= 但由于“不完整类型”,它不能编译最可调用的类型:

template<typename T>
Event & operator-=(const T &eventHandler)
{
    m_eventHandlers.remove_if([&](const EventHandler &_eventHandler)
    {
        if (_eventHandler.template target<T>() != null_ptr) // <- ERROR
        {
            // ...
        }
    });

    return *this;
}    

我也尝试将 T* 作为 target() 的模板参数,但我被卡住了。是否可以编写一个通用 operator-= 来正确比较任何可调用类型?

提前致谢!

【问题讨论】:

  • 既然您评论说您保证目标具有可比性,您可能希望使用自己的类型擦除解决方案(这需要基础类型,即std::function lingo 中的目标)来建模例如 EqualityComparable)。

标签: c++ lambda c++11 function-pointers std-function


【解决方案1】:

问题在于std::function&lt;&gt; 不支持比较运算符==!=boost::function has an explanation for that.

一种解决方案是将函数存储在映射或以整数为键的哈希中。注册将返回该整数键。要取消注册,必须传递密钥,例如:

typedef int handler_id_t;
handler_id_t add(const EventHandler &eventHandler);
void del(handler_id_t);

【讨论】:

  • +1... 虽然令人失望的是function 即使在它的目标是不可比较的情况下,并且没有办法围绕它编写代码,但真正的问题可能不是测试回调是否相等。如果两个客户端注册了完全相同的方法,应该调用一次还是两次?
  • 感谢您的快速回答,Maxim。但是,您的建议是我真正想通过使用 C++11 提供的一些语法糖来避免的。虽然 std::function 不支持比较,但目标应该是可比较的。在某些时候,我希望所有可调用类型都归结为指向可执行代码的指针,就像函数指针一样。至少我希望如此。因此,找到一种以通用方式正确调用 std::function.target() 的方法会很棒。
  • @Potatoswatter 那么它应该被调用两次。例如,在我的代码示例中,您可以添加两次 eventHandler() 并执行两次。 operator-= 会删除所有出现的事件。但是,当目标比较问题解决时,可以测试是否已经在更完整的事件类中添加了回调。
  • @MaximYegorushkin 因此,结论是原则上这是不可能的,因为我们无法获得指向所有可调用类型的代码的指针,对吧?太糟糕了。谢谢你们的回答,伙计们。
  • @JackJohansson:“在某些时候,我希望所有可调用类型都归结为指向可执行代码的指针,就像函数指针一样。”这并不意味着它们是平等可比的。仅仅因为你有一个碰巧解析到相同成员函数的函子并不意味着它们存储相同的数据。相同类型的两个仿函数将具有相同的成员指针,但可以存储不同的数据。它们不一定相等。这就是为什么 Boost 的信号/插槽库公开了一个特殊对象,您可以使用它来破坏连接(如果您愿意的话)。
猜你喜欢
  • 1970-01-01
  • 2012-12-15
  • 2023-03-22
  • 2020-06-01
  • 2015-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多