C++ std::函数作为回调函数,是否可以取消注册?
问题严格地说是关于C++ std::函数作为回调函数,是否可以取消注册?,c++,c++11,C++,C++11,问题严格地说是关于std::function,而不是boost::function。有关更多详细信息,请参阅此问题底部的更新部分,特别是关于无法按照C++11标准比较非空std::function对象的部分 C++11std::function类模板非常适合维护回调集合。例如,可以将它们存储在向量中,并在需要时调用它们。然而,维护这些对象并允许注销似乎是不可能的 让我具体地说,想象一下这个类: class Invoker { public: void Register(std::funct
std::function
,而不是boost::function
。有关更多详细信息,请参阅此问题底部的更新部分,特别是关于无法按照C++11标准比较非空std::function
对象的部分
C++11
std::function
类模板非常适合维护回调集合。例如,可以将它们存储在向量中,并在需要时调用它们。然而,维护这些对象并允许注销似乎是不可能的
让我具体地说,想象一下这个类:
class Invoker
{
public:
void Register(std::function<void()> f);
void Unregister(std::function<void()> f);
void InvokeAll();
private:
// Some container that holds the function objects passed to Register()
};
类调用程序
{
公众:
无效寄存器(std::函数f);
作废注销(std::函数f);
void InvokeAll();
私人:
//保存传递给Register()的函数对象的容器
};
示例使用场景:
void foo()
{
}
int main()
{
std::function<void()> f1{foo};
std::function<void()> f2{[] {std::cout << "Hello\n";} };
Invoker inv;
// The easy part
// Register callbacks
inv.Register(f1);
inv.Register(f2);
// Invoke them
inv.InvokeAll();
// The seemingly impossible part. How can Unregister() be implemented to actually
// locate the correct object to unregister (i.e., remove from its container)?
inv.Unregister(f2);
inv.Unregister(f1);
}
void foo()
{
}
int main()
{
std::函数f1{foo};
std::function f2{[]{std::cout由于无法测试容器中的元素标识,因此最好使用容器(如std::list
),其迭代器在修改容器时不会失效,并将迭代器返回给注册调用方,以用于注销
如果确实要使用向量
(或deque
),您可以在添加回调时将整数索引返回到vector/deque中。此策略自然要求您确保索引以这种方式可用,以标识函数在序列中的位置。如果回调和/或注销很少,这可能意味着不重用spot。或者,您可以实现free list以重用空插槽。或者,仅从序列的末尾回收空插槽,并保持基本索引偏移量,该偏移量在从开始回收插槽时增加
如果您的回调访问模式不需要随机访问遍历,那么将回调存储在std::list
中并使用原始迭代器取消注册对我来说似乎是最简单的。我有一个想法
将回调存储为std::weak_ptr
。然后调用者负责保持相应的std::shared_ptr
处于活动状态,调用者取消注册回调所需做的一切就是将所有活动的std::shared_ptr
销毁到回调函数
调用回调时,代码必须仔细检查它正在使用的std::weak_ptr
s上的锁失败。当它运行在这些锁上时,可以将它们从注册回调的容器中删除
请注意,这并不能提供完整的线程安全性,因为回调调用程序可以锁定std::weak_ptr
,并临时激活回调函数的std::shared_ptr
,在调用程序的std::shared_ptr
超出范围后,该函数可以保持活动状态。您可以像MS那样处理连接点:返回cookie(索引到向量中)调用方提供撤销注册。我通常将接口指针存储为回调并将它们放入集合中。然后,我可以按值访问注销。@罗杰罗兰德好主意。@ WooZravig Boost也为他们的一个类做了。在做了C++程序员17年之后,我终于知道了使用<代码> STD::列表< /COD>本身的理由。