【问题标题】:remove any element of vector<std::function<...>> that bound to member function删除绑定到成员函数的 vector<std::function<...>> 的任何元素
【发布时间】:2012-06-27 13:35:58
【问题描述】:

如何删除绑定到this对象的成员函数的函数:

std::vector<std::function<void(int)>> callbacks;

class MyClass {
public:
    MyClass() {
        callbacks.push_back(
            std::bind(&MyClass::myFunc,this,std::placeholders::_1)
        );
    }
    ~MyClass() {
        auto it = std::remove_if( std::begin(callbacks),
                                  std::end(callbacks),
                                  [&](std::function<void(int)>& f) {
                return // <-- this is my question
                       //     true (remove) if f is bound to member function 
                       //     of this
        });
        callbacks.erase(it,std::end(callbacks));
    }
    void myFunc(int param){...}
};

【问题讨论】:

  • @K-ballo 你的意思是“无法取回该信息”吗?
  • 是的,对不起... 函数被某种类型擦除抽象出来
  • 建议在没有资格的情况下致电(免费)begin 和 end,让 ADL 完成工作,以防容器不符合标准的容器,成员 begin 和 @987654326 @.

标签: c++ vector c++11 bind std-function


【解决方案1】:
    typedef decltype(std::bind(&MyClass::myFunc,this,std::placeholders::_1)) bound_type;

    auto it = std::remove_if( std::begin(callbacks),
                              std::end(callbacks),
                              [](const std::function<void(int)>& f) {
          return f.target<bound_type>() != nullptr;
    });

成员函数模板std::function::target&lt;T&gt;如果是T类型,则返回指向目标对象的指针,否则返回null。所以你只需要能够命名目标对象的类型,你可以从decltype得到。真的很简单:-)

注意这将删除该类型的 any 回调,而不仅仅是那些为被销毁的特定对象绑定了this 指针的回调。如果您试图阻止在对象被销毁后对其调用回调并且无法识别向量的哪些元素引用了哪些对象,您可以考虑将 shared_ptr 放在您的类中,然后将 weak_ptr 存储到它回调,可用于检测对象是否已被销毁:

class MyClass
{
    struct NullDeleter { void operator()(void*) const { } };
    std::shared_ptr<MyClass> sp;

    static void safe_invoke(void (MyClass::*f)(int), const std::weak_ptr<MyClass>& wp, int i)
    {
        if (std::shared_ptr<MyClass> safe_this = wp.lock())
            (safe_this.get()->*f)(i);
    }

public:
    MyClass() : sp(this, NullDeleter()) {
        callbacks.push_back(
            std::bind(safe_invoke, &MyClass::myFunc ,std::weak_ptr<MyClass>(sp),
                      std::placeholders::_1)
        );
    };

这使用invoke 函数包装对成员函数的调用,该函数在调用成员函数之前将weak_ptr 转换为shared_ptr。如果对象已被销毁,shared_ptr 将为空,因此该函数不执行任何操作。当回调变得无效时,这实际上并不会删除回调,但确实可以安全地调用。

【讨论】:

  • 我们可以让谓词适用于任何成员函数吗?也就是说,如果我在构造函数上将MyClass::myFunc1 和MyClass::myFunc2 推送到callbacks 向量,那么这两个函数都从向量中删除MyClass析构函数
  • @uray:不,它们是不同类型的函数指针;您需要单独删除每个。
  • @uray,仅当两个成员函数具有相同的类型时(因为如果它们这样做,那么 bind 返回的调用包装器也将具有相同的类型,即使它包装了不同的实例类型)。如果您知道所有类的成员函数的类型,那么您可以让 lambda 表达式检查所有可能的类型:return f.target&lt;bound_type1&gt;() != nullptr &amp;&amp; f.target&lt;bound_type1&gt;() != nullptr &amp;&amp; 等
【解决方案2】:

在一般情况下,如果没有大量额外的工作,您将无法做到。类型擦除会从对象中清除此信息,std::function 不会直接公开此信息。

您的具体示例可能只有一个成员函数可以作为候选删除,但是一个有 5 个成员可以存储为回调的类呢?您需要测试所有这些,并且还可以使用 lambda 绑定成员函数,这几乎无法检测到。

这是一种解决方案,如果:

  • 所有回调都是从MyClass 中注册的
  • 修改了容器以存储额外信息
  • 你愿意做所有额外的簿记
std::vector<std::pair<std::function<void(int)>, void*>> callbacks;

class MyClass{
  static unsigned const num_possible_callbacks = 2; // keep updated
  std::array<std::type_info const*, num_possible_callbacks> _infos;
  unsigned _next_info;

  // adds type_info and passes through
  template<class T>
  T const& add_info(T const& bound){
    if(_next_info == num_possible_callbacks)
      throw "oh shi...!"; // something went out of sync
    _infos[_next_info++] = &typeid(T);
    return bound;
  }
public:
  MyClass() : _next_info(0){
    using std::placeholders::_1;
    callbacks.push_back(std::make_pair(
        add_info(std::bind(&MyClass::myFunc, this, _1)),
        (void*)this));
    callbacks.push_back(std::make_pair(
        add_info([this](int i){ return myOtherFunc(i, 0.5); }),
        (void*)this));
  }

  ~MyClass(){
    using std::placeholders::_1;

    callbacks.erase(std::remove_if(callbacks.begin(), callbacks.end(),
        [&](std::pair<std::function<void(int)>, void*> const& p) -> bool{
          if(p.second != (void*)this)
            return false;
          auto const& f = p.first;
          for(unsigned i = 0; i < _infos.size(); ++i)
            if(_infos[i] == &f.target_type())
              return true;
          return false;
        }), callbacks.end());
  }

  void myFunc(int param){ /* ... */ }
  void myOtherFunc(int param1, double param2){ /* ... */ }
};

Live example on Ideone.

【讨论】:

  • 类型没有被完全擦除;其type_info 可从function::target_type() 获得。我不认为你可以用它来做 OP 想要的,但我可能是错的。
  • @Mike:没错,也许您可​​以解析type_info::name() 值,但由于这是特定于实现的,我认为它不会有任何好处。
  • std::function 绝对确实公开信息,请参阅我的回答。如果你知道它的类型,你就可以访问目标对象。
  • @Jonathan:嗯,不是真的。如果您已经知道它,它会公开目标类型,但它不会公开它是否是绑定的成员函数。如果你已经知道目标类型是std::function 商店,那还有什么理由再问呢?真的,这没有任何意义,因此我认为投反对票是非常不合理的。
  • 认为您可能不知道任何特定 std::function 的类型但知道具有给定目标类型的对象在某处的列表中是不合理的,您只是不知道哪个元素的序列是什么呢?哦,等等,这正是问题所在。你说“你不能”,我的回答证明你可以,所以你的回答没有用。
【解决方案3】:

我曾经需要做这样的事情,我通过在包含函数的类中存储对象的共享指针向量来解决它,并在它们被销毁时按值从向量中删除函数,这也使得这个自动化.

【讨论】:

    猜你喜欢
    • 2012-08-22
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-12
    • 2015-03-04
    相关资源
    最近更新 更多