C++如何从std::vector<std::function<...>>中移除一个std::function?

6

我有以下回调系统

class ... {

    ...

    std::vector<std::function<void()>>        systemBringUps;
    std::vector<std::function<void()>>        systemTearDowns;
    std::vector<std::function<void(EntityID_t)>> systemMains;
    std::vector<std::function<bool(EntityID_t)>> systemChecks;

    template <typename T>
    void registerSystem() {
        systemBringUps.push_back(T::systemBringUp);
        systemTearDowns.push_back(T::systemTearDown);
        systemMains.push_back(T::systemMain);
        systemChecks.push_back(T::systemCheck);
        T::onSystemRegister();
    }

    template <typename T>
    void deregisterSystem() {
        std::function<void()> bringUp = T::systemBringUp;
        std::function<void()> tearDown = T::systemTearDown;
        std::function<void(EntityID_t)> smain = T::systemMain;
        std::function<bool(EntityID_t)> check = T::systemCheck;

        std::remove(systemBringUps.begin(), systemBringUps.end(), bringUp);
        std::remove(systemTearDowns.begin(), systemTearDowns.end(), tearDown);
        std::remove(systemMains.begin(), systemMains.end(), smain);
        std::remove(systemChecks.begin(), systemChecks.end(), check);
        T::onSystemDeregister();
    }

registerSystem模板函数运行良好,但deregisterSystem函数编译失败,显示以下错误信息:

...

/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0/bits/predefined_ops.h:241:17: error: invalid operands to binary expression
      ('std::function<bool (unsigned short)>' and 'const std::function<bool (unsigned short)>')
        { return *__it == _M_value; }
                 ~~~~~ ^  ~~~~~~~~
...

看起来像是std::remove无法编译,因为我定义的deregisterSystem中的std::functionconst的,而我尝试从中删除的向量不是? 有没有办法从这些std :: function中丢弃const修饰符? 我尝试使用复制构造函数,如下所示:

template <typename T>
    void deregisterSystem() {
        std::function<void()> bringUp(T::systemBringUp);
        std::function<void()> tearDown(T::systemTearDown);
        std::function<void(EntityID_t)> smain(T::systemMain);
        std::function<bool(EntityID_t)> check(T::systemCheck);

        std::remove(systemBringUps.begin(), systemBringUps.end(), bringUp);
        std::remove(systemTearDowns.begin(), systemTearDowns.end(), tearDown);
        std::remove(systemMains.begin(), systemMains.end(), smain);
        std::remove(systemChecks.begin(), systemChecks.end(), check);
        T::onSystemDeregister();
    }

但是这种方法同样会失败。
2个回答

7
你的问题并不是constness,而是 std::function 对象根本无法进行相等比较。你当前的方法根本行不通。
解决方案是在注册时为调用者提供某种唯一的“令牌”,以便以后用于注销。

哦,那是因为我不够了解C++!非常感谢你的提示。 - riley lyman

4
你可以使用 std::function<F>::target 直接获取存储的函数对象并进行比较。
例如,要从 systemBringUps 中删除所有的函数 T::systemBringUp,你可以这样做:
systemBringUps.erase(std::remove_if(systemBringUps.begin(), systemBringUps.end(), [](const auto& f) {
    auto* target = f.template target<decltype(T::systemBringUp)>();
    return target != nullptr && *target == T::systemBringUp;
}), systemBringUps.end());

你可以编写一个辅助函数来检查 std::function 是否持有一个值:
template<class F, class T>
bool is_function_holding(const std::function<F>& func, const T& target) {
    static_assert(std::is_assignable<std::function<F>&, const T&>::value, "Function could not possibly hold target");
    auto* current_target = func.template target<typename std::decay<T>::type>();
    return current_target != nullptr && *current_target == target;
}

// Later
systemBringUps.erase(std::remove_if(systemBringUps.begin(), systemBringUps.end(), [](const auto& f) {
    return is_function_holding(f, T::systemBringUp);
}, systemBringUps.end());
systemTearDowns.erase(std::remove_if(systemTearDowns.begin(), systemTearDowns.end(), [](const auto& f) {
    return is_function_holding(f, T::systemTearDown);
}, systemTearDowns.end());
// ...

这非常有帮助,感谢您提供一些示例实现! - riley lyman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接