You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现可存入同一容器的成员函数指针的比较功能?

问题描述

我想把成员函数指针存储到某个对象里,之后在程序中和其他成员函数指针做比较。要求存储指针的对象是具体类型,而且能放进同一个容器里。

我试过用类型擦除(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"
}
解决方案

可以基于类型擦除重新设计,绕过模板函数不能作为虚函数的限制,同时提供可存入容器的具体类型。具体实现如下:

核心思路

  1. 定义抽象基类FuncBase,提供纯虚的比较接口和类型标识接口,作为统一的对外层。
  2. 模板子类FuncImpl<T>继承自FuncBase,存储具体的成员函数指针,实现基类的虚函数完成比较逻辑。
  3. 非模板包装类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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 18:55:12