如何实现可存入同一容器的成员函数指针的比较功能?
问题描述
我想把成员函数指针存储到某个对象里,之后在程序中和其他成员函数指针做比较。要求存储指针的对象是具体类型,而且能放进同一个容器里。
我试过用类型擦除(Type-erasure)方案,但失败了——因为模板函数不能声明为虚函数,没法提供可重写的compareWith接口。
我搞了个半可行的方案,但问题在于Fnc对象的类型各不相同,没法存入同一个容器,可我确实需要把它们放进同一个容器里。
以下是简化后的代码示例:
template<class T> class Fnc { public: Fnc(T&& fnc) : m_fnc(std::forward<T>(fnc)) {} template<class Y> bool compareWith(const Fnc<Y>& other) { return other.m_fnc == m_fnc; } private: T m_fnc; }; class MyClass { public: void method1(int a); void method2(int a); void method3(); }; int main() { // 这段能正常运行,因为没尝试把Fnc对象放进容器 Fnc fnc1(&MyClass::method1); Fnc fnc2(&MyClass::method2); printf("%d\n", fnc1.compareWith(Fnc(&MyClass::method1))); // 输出"1" printf("%d\n", fnc1.compareWith(Fnc(&MyClass::method3))); // 输出"0" printf("%d\n", fnc2.compareWith(Fnc(&MyClass::method2))); // 输出"1" // 这段无法运行,因为Fnc不是多态类型(也没法做成多态——模板函数不能是虚函数) std::vector<Fnc> methods; methods.push_back(Fnc(&MyClass::method1)); methods.push_back(Fnc(&MyClass::method2)); methods.push_back(Fnc(&MyClass::method3)); printf("%d\n", methods[0].compareWith(Fnc(&MyClass::method1))); // 应该输出"1" printf("%d\n", methods[1].compareWith(Fnc(&MyClass::method2))); // 应该输出"1" printf("%d\n", methods[2].compareWith(Fnc(&MyClass::method3))); // 应该输出"1" printf("%d\n", methods[0].compareWith(Fnc(&MyClass::method2))); // 应该输出"0" }
解决方案
可以基于类型擦除重新设计,绕过模板函数不能作为虚函数的限制,同时提供可存入容器的具体类型。具体实现如下:
核心思路
- 定义抽象基类
FuncBase,提供纯虚的比较接口和类型标识接口,作为统一的对外层。 - 模板子类
FuncImpl<T>继承自FuncBase,存储具体的成员函数指针,实现基类的虚函数完成比较逻辑。 - 非模板包装类
Func持有FuncBase的智能指针,对外暴露统一的比较方法,作为容器的元素类型。
完整代码
#include <memory> #include <vector> #include <typeindex> #include <cstdio> // 抽象基类,提供统一的比较和类型查询接口 class FuncBase { public: virtual ~FuncBase() = default; virtual bool equals(const FuncBase* other) const = 0; virtual std::type_index getType() const = 0; }; // 模板子类,存储具体的成员函数指针并实现比较逻辑 template<typename T> class FuncImpl : public FuncBase { public: explicit FuncImpl(T ptr) : m_funcPtr(ptr) {} bool equals(const FuncBase* other) const override { // 先判断类型是否匹配,不匹配直接返回false if (getType() != other->getType()) { return false; } // 类型匹配后强转并比较指针值 const auto* otherImpl = static_cast<const FuncImpl<T>*>(other); return m_funcPtr == otherImpl->m_funcPtr; } std::type_index getType() const override { return std::type_index(typeid(T)); } private: T m_funcPtr; }; // 非模板包装类,作为容器的具体元素类型 class Func { public: // 接收任意类型的函数指针,构造对应的FuncImpl对象 template<typename T> explicit Func(T ptr) : m_impl(std::make_unique<FuncImpl<T>>(ptr)) {} // 对外统一的比较接口 bool compareWith(const Func& other) const { return m_impl->equals(other.m_impl.get()); } private: std::unique_ptr<FuncBase> m_impl; }; class MyClass { public: void method1(int a) {} void method2(int a) {} void method3() {} }; int main() { std::vector<Func> methods; methods.emplace_back(&MyClass::method1); methods.emplace_back(&MyClass::method2); methods.emplace_back(&MyClass::method3); printf("%d\n", methods[0].compareWith(Func(&MyClass::method1))); // 输出1 printf("%d\n", methods[1].compareWith(Func(&MyClass::method2))); // 输出1 printf("%d\n", methods[2].compareWith(Func(&MyClass::method3))); // 输出1 printf("%d\n", methods[0].compareWith(Func(&MyClass::method2))); // 输出0 }
关键说明
- 用
std::type_index快速判断两个函数指针的类型是否一致,避免无效的类型转换,提升比较效率。 Func是具体类型,可直接存入std::vector等容器,对外提供统一的compareWith方法。- 使用
std::unique_ptr管理子类对象,自动处理内存释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者TheBullet0070
相关产品推荐
相关产品推荐

