C++ 从 std::vector<std::function<...>> 中删除 std::function

ril*_*man 6 c++ c++11

我有以下回调系统

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();
    }
Run Code Online (Sandbox Code Playgroud)

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; }
                 ~~~~~ ^  ~~~~~~~~
...
Run Code Online (Sandbox Code Playgroud)

所以似乎std::remove无法编译,因为我定义的std::functionsderegisterSystemconst,而我试图从中删除的向量中的那些不是?有什么办法可以const从这些std::functions 中丢弃限定符吗?我试过使用复制构造函数

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();
    }
Run Code Online (Sandbox Code Playgroud)

但这以类似的方式失败。

orl*_*rlp 7

您的问题不在于常量性,而是std::function根本无法比较对象的相等性。你目前的方法根本行不通。

解决方案是在注册期间向调用者提供某种独特的“令牌”,可用于稍后取消注册。


Art*_*yer 4

您可以使用std::function<F>::target直接获取保存的函数对象并进行比较。

例如,要删除所有功能T::systemBringUp例如,要删除中systemBringUps,您可以执行以下操作:

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());
Run Code Online (Sandbox Code Playgroud)

您可以创建一个辅助函数来检查 a 是否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());
// ...
Run Code Online (Sandbox Code Playgroud)