C++17中为各类可调用对象生成唯一ID的实现方案
实现异构可调用容器的
contains函数 针对需求——在存储Function指针的异构容器中判断是否包含指定可调用对象(支持函数指针、成员函数指针、可调用类,lambda始终返回false,舍弃std::function支持),以下是可行实现方案:
1. 扩展基类Function,添加匹配接口
给抽象基类新增纯虚函数matches,用于类型擦除后的匹配判断:
class Function { public: Function(std::string name) : m_name(name) {} virtual ~Function(){} std::string name() { return m_name; } template <typename... Args> decltype(auto) operator()(Args&&... args) { // 原有委托逻辑保留,此处简化 return; } // 新增:判断当前可调用对象是否与目标匹配 virtual bool matches(const std::any& target) const = 0; private: std::string m_name; virtual std::any invoke(std::initializer_list<std::any> const& args) const = 0; };
2. 在FunctionImpl中实现匹配逻辑
针对具体可调用类型F,重写matches函数,尝试将传入的std::any转换为F类型后做相等比较:
template <typename F> class FunctionImpl : public Function { public: FunctionImpl(F const& f, std::string name) : Function(name) , function(f) {} private: std::any invoke(std::initializer_list<std::any> const& args) const override { return std::any(); } bool matches(const std::any& target) const override { // 尝试将目标转换为当前可调用类型 if (const auto* target_ptr = std::any_cast<const F>(&target)) { // 函数指针/成员函数指针直接比较地址;可调用类需支持==运算符 return *target_ptr == function; } return false; } F function; };
3. 实现contains函数
遍历容器,调用每个Function对象的matches方法,同时对lambda直接返回false:
首先定义lambda类型判断工具:
#include <type_traits> template <typename T> struct is_lambda : std::false_type {}; // 通用lambda判断:匹配带operator()的匿名类类型(排除函数指针、成员函数指针等) template <typename T> struct is_lambda<T> : std::integral_constant<bool, std::is_class_v<std::decay_t<T>> && !std::is_function_v<std::decay_t<T>> && !std::is_member_function_pointer_v<std::decay_t<T>> && requires(T t) { t.operator()(); }> {}; template <typename T> constexpr bool is_lambda_v = is_lambda<T>::value;
然后实现contains:
template <typename F> bool contains(FContainer const& vec, F const& f) { // 按照要求:lambda的contains始终返回false if constexpr (is_lambda_v<F>) { return false; } else { std::any target = f; for (const auto& func : vec) { if (func->matches(target)) { return true; } } return false; } }
4. 补充可调用类的相等运算符
自定义可调用类需要显式定义operator==才能支持匹配判断:
struct ACallableClass { void operator()(){} // C++20及以上可使用default生成默认相等逻辑 bool operator==(const ACallableClass&) const = default; };
完整测试代码
#include <any> #include <string> #include <memory> #include <vector> #include <cassert> #include <type_traits> // lambda类型判断工具 template <typename T> struct is_lambda : std::false_type {}; template <typename T> struct is_lambda<T> : std::integral_constant<bool, std::is_class_v<std::decay_t<T>> && !std::is_function_v<std::decay_t<T>> && !std::is_member_function_pointer_v<std::decay_t<T>> && requires(T t) { t.operator()(); }> {}; template <typename T> constexpr bool is_lambda_v = is_lambda<T>::value; class Function { public: Function(std::string name) : m_name(name) {} virtual ~Function(){} std::string name() { return m_name; } template <typename... Args> decltype(auto) operator()(Args&&... args) { return; } virtual bool matches(const std::any& target) const = 0; private: std::string m_name; virtual std::any invoke(std::initializer_list<std::any> const& args) const = 0; }; template <typename F> class FunctionImpl : public Function { public: FunctionImpl(F const& f, std::string name) : Function(name) , function(f) {} private: std::any invoke(std::initializer_list<std::any> const& args) const override { return std::any(); } bool matches(const std::any& target) const override { if (const auto* target_ptr = std::any_cast<const F>(&target)) { return *target_ptr == function; } return false; } F function; }; using FPointer = std::unique_ptr<Function>; template <typename F> FPointer make_function(F const& f, std::string name) { return std::make_unique<FunctionImpl<F>>(f, name); } using FContainer = std::vector<FPointer>; template <typename F> bool contains(FContainer const& vec, F const& f) { if constexpr (is_lambda_v<F>) { return false; } else { std::any target = f; for (const auto& func : vec) { if (func->matches(target)) { return true; } } return false; } } void bar(){} void foo(){} struct AClass { void MemberFunction1(){} void MemberFunction2(){} }; struct ACallableClass { void operator()(){} bool operator==(const ACallableClass&) const = default; }; int main() { FContainer v; v.push_back(make_function(&foo, "foo")); v.push_back(make_function(&AClass::MemberFunction1, "AClass::MemberFunction1")); v.push_back(make_function(ACallableClass(), "CallableClass")); v.push_back(make_function([](){}, "empty lambda")); assert(contains(v, &foo)); assert(contains(v, &AClass::MemberFunction1)); assert(!contains(v, [](){})); assert(!contains(v, &bar)); assert(!contains(v, &AClass::MemberFunction2)); return 0; }
关键说明
- 类型擦除匹配:通过
std::any实现类型安全的跨类型匹配,FunctionImpl仅处理自身存储的可调用类型,避免类型错误。 - lambda处理:利用类型 trait 识别lambda类型,直接返回false,符合需求。
- 相等比较:函数指针、成员函数指针直接比较内存地址;可调用类需显式定义
operator==(C++20可使用=default生成默认实现)。
内容的提问来源于stack exchange,提问作者joergbrech
相关产品推荐
相关产品推荐

