C++中从vector移除std::function<void()>的最优方案咨询
C++发布订阅类中std::function无法比较导致取消订阅失败的解决方案选择
我正在实现一个名为SystemInterface的发布订阅类,负责接收实例更新并推送给订阅者。添加订阅回调的逻辑正常,但取消订阅时,由于C++中std::function<void()>不支持比较操作,导致代码报错。
错误代码示例:
std::vector<std::function<void()>> subs; void subscribe(std::function<void()> f) { subs.push_back(f); } void unsubscribe(std::function<void()> f) { std::remove(subs.begin(), subs.end(), f); // 此处报错:std::function无比较运算符 }
我梳理出五种解决方案,以下是各方案的优缺点分析,结合现有多层映射结构的订阅实现,寻求最优选择:
各方案优缺点分析
方案1:使用weak_ptr注册函数,订阅者持有返回的shared_ptr
- 优点:自动管理回调生命周期,避免订阅者销毁后出现悬空回调;可通过shared_ptr的地址实现比较查找
- 缺点:订阅者需额外持有返回的shared_ptr,增加使用成本;多层映射下需维护ptr与回调的关联,结构复杂度提升
方案2:用自定义唯一键映射回调函数替代vector存储
- 优点:取消订阅仅需通过唯一键操作,逻辑简洁;天然适配多层映射结构,可直接扩展为嵌套键值对(如
Topic -> Key -> Callback);无类型限制,支持所有可调用对象 - 缺点:需要生成并管理唯一键,订阅者必须保存对应的键值;键生成规则不当可能出现重复或泄漏问题
- 优点:取消订阅仅需通过唯一键操作,逻辑简洁;天然适配多层映射结构,可直接扩展为嵌套键值对(如
方案3:使用函数指针的vector
- 优点:函数指针支持直接比较,取消订阅逻辑简单;性能开销极低
- 缺点:仅支持全局/静态函数,无法兼容带捕获的lambda、成员函数等需要上下文的回调,灵活性严重不足,几乎无法满足实际业务需求
方案4:通过获取地址使回调函数可比较
- 优点:实现简单,同一std::function实例的地址可直接比较
- 缺点:仅当取消订阅传入的是完全相同的std::function实例时有效,临时构造的std::function或lambda无法匹配;实际使用场景受限极大
方案5:使用接口类调用虚函数,注册ServiceCore引用
- 优点:面向接口设计,扩展性强;通过对象引用/指针比较,取消订阅逻辑可靠
- 缺点:要求订阅者必须实现指定接口类,侵入性强;多层映射下需维护对象与接口的关联,代码复杂度较高
针对多层映射结构的最优建议
如果你的现有实现已经是多层映射结构,**方案2(自定义唯一键映射)**是最适配的选择:
- 多层映射本身就是键值对结构,引入唯一键后可直接扩展为
主题类型 -> 唯一键 -> 回调函数的嵌套结构,与现有实现无缝兼容 - 取消订阅操作仅需指定主题和对应键,逻辑清晰且效率高,无需处理复杂的类型或生命周期问题
- 唯一键的生成可灵活选择:自增整数、UUID,或由订阅者提供自定义标识,适配不同业务场景
如果你的场景需要兼顾回调生命周期管理(如订阅者可能提前销毁),可以结合方案1与方案2:用唯一键映射weak_ptr<std::function<...>>,发布更新时先通过lock()检查回调有效性,取消订阅仍通过键操作,既保证了生命周期安全,又保留了键值映射的便捷性。
内容的提问来源于stack exchange,提问作者Hamza Hajeir
相关产品推荐
相关产品推荐

