如何在vector中查找并移除lambda?遇operator==匹配错误求解
解决vector中std::variant包含std::function时的查找删除错误
问题描述
尝试在存储std::variant<std::function<void()>, Subscription*>的vector中查找并移除元素时,出现如下编译错误:
no match for 'operator==' (operand types are 'const std::function<void()>' and 'const std::function<void()>')
相关代码:
using TeardownLogic = variant<function<void()>, Subscription*>; class Example { vector<TeardownLogic> finalizers = vector<TeardownLogic>(); void add(TeardownLogic teardown) { this->finalizers.push_back(teardown); } void remove(TeardownLogic teardown) { auto idx = find(this->finalizers.begin(), this->finalizers.end(), teardown); if (idx != this->finalizers.end()) { this->finalizers.erase(idx); } } };
原因分析
std::function默认不支持直接用==比较:
- 若存储的是普通函数指针,可以比较;但如果是lambda(尤其是带捕获的lambda),其闭包类型无法生成合法的
operator==,导致std::variant的默认相等比较失效。 std::find依赖元素的operator==,当variant持有std::function时,就会触发上述编译错误。
解决方案
方案一:用唯一标识包装可调用对象(最可靠)
给每个std::function分配唯一ID,通过ID来匹配元素,避免直接比较std::function:
#include <atomic> struct TaggedFunc { static std::atomic_size_t next_id; size_t id; std::function<void()> func; TaggedFunc(std::function<void()> f) : id(next_id++), func(std::move(f)) {} }; std::atomic_size_t TaggedFunc::next_id = 0; using TeardownLogic = std::variant<TaggedFunc, Subscription*>; class Example { std::vector<TeardownLogic> finalizers; void add(TeardownLogic teardown) { finalizers.push_back(std::move(teardown)); } void remove(const TeardownLogic& teardown) { auto idx = std::find_if(finalizers.begin(), finalizers.end(), [&teardown](const TeardownLogic& elem) { if (std::holds_alternative<TaggedFunc>(elem) && std::holds_alternative<TaggedFunc>(teardown)) { return std::get<TaggedFunc>(elem).id == std::get<TaggedFunc>(teardown).id; } else if (std::holds_alternative<Subscription*>(elem) && std::holds_alternative<Subscription*>(teardown)) { return std::get<Subscription*>(elem) == std::get<Subscription*>(teardown); } return false; }); if (idx != finalizers.end()) { finalizers.erase(idx); } } };
方案二:仅针对无捕获lambda/函数指针的场景适配
如果你的std::function只存储无捕获lambda或普通函数指针,可以通过target()方法获取底层指针进行比较:
void remove(const TeardownLogic& teardown) { auto idx = std::find_if(finalizers.begin(), finalizers.end(), [&teardown](const TeardownLogic& elem) { if (std::holds_alternative<std::function<void()>>(elem) && std::holds_alternative<std::function<void()>>(teardown)) { const auto& func_elem = *std::get<std::function<void()>>(elem); const auto& func_teardown = *std::get<std::function<void()>>(teardown); // 仅当存储的是函数指针或无捕获lambda时有效 return func_elem.target<void(*)()>() == func_teardown.target<void(*)()>(); } else if (std::holds_alternative<Subscription*>(elem) && std::holds_alternative<Subscription*>(teardown)) { return std::get<Subscription*>(elem) == std::get<Subscription*>(teardown); } return false; }); if (idx != finalizers.end()) { finalizers.erase(idx); } }
注意:带捕获的lambda无法用这种方法比较,会导致匹配失败。
方案三:替换std::function为可比较的类型
如果业务允许,可以放弃std::function,改用明确的函数指针+对象指针组合,比如:
using TeardownFunc = std::pair<Subscription*, void(Subscription::*)()>; using TeardownLogic = std::variant<TeardownFunc, Subscription*>;
这种情况下可以直接通过指针比较来匹配元素。
内容的提问来源于stack exchange,提问作者Get Off My Lawn
相关产品推荐
相关产品推荐

