如何向持有Derived类型的类传递接收Base类型的成员函数
解决模板类中存储适配Base类的成员函数指针问题
嘿,这个问题本质是C++模板场景下,处理派生类模板类适配基类参数成员函数指针的典型问题,我给你几个实用的解决方案,帮你实现“仅传递成员函数指针给另一类”的目标:
1. 直接调整模板类的成员函数指针类型(最直接)
既然你的成员函数是接受Base类参数,而模板类的参数是Derived(继承自Base),那我们可以直接在模板类里定义适配Base参数的成员函数指针类型,同时用类型约束确保模板参数确实是Base的派生类。
示例代码:
#include <type_traits> // 基类与派生类定义 class Base {}; class Derived : public Base {}; // 模板Wrapper类,专门存储接受Base参数的Derived类成员函数指针 template <typename DerivedT, typename BaseT = Base> requires std::is_base_of_v<BaseT, DerivedT> // 确保DerivedT是BaseT的派生类 class Wrapper { public: // 定义成员函数指针类型:DerivedT的成员函数,接受BaseT&参数 using MemberFuncPtr = void (DerivedT::*)(BaseT&); // 构造函数直接接收成员函数指针 Wrapper(MemberFuncPtr func) : m_func(func) {} // 调用成员函数的方法 void invoke(DerivedT& obj, BaseT& arg) { (obj.*m_func)(arg); } private: MemberFuncPtr m_func; }; // 使用示例 class MyHandler { public: // 这个成员函数接受Base类参数,符合Wrapper的要求 void onEvent(Base& event) { // 处理逻辑 } }; int main() { // 直接传递成员函数指针给Wrapper Wrapper<MyHandler> wrapper(&MyHandler::onEvent); MyHandler handler; Base event; wrapper.invoke(handler, event); }
这个方案最简洁,因为我们直接让模板类的成员函数指针类型匹配目标函数的签名,同时用requires约束避免非法的模板参数。
2. 用std::function做类型擦除(更灵活)
如果你的场景需要更灵活的适配(比如兼容不同签名的成员函数),可以用std::function做类型擦除,配合lambda把成员函数指针包装成统一的调用接口。
示例代码:
#include <functional> #include <type_traits> class Base {}; class Derived : public Base {}; template <typename DerivedT> class Wrapper { public: // 定义统一的函数类型:接受DerivedT对象引用和Base对象引用 using Callable = std::function<void(DerivedT&, Base&)>; // 构造函数直接接受包装好的Callable Wrapper(Callable func) : m_func(std::move(func)) {} // 重载构造函数,直接接受成员函数指针并自动包装 template <typename ArgT> requires std::is_base_of_v<Base, ArgT> // 确保参数是Base的派生类 Wrapper(void (DerivedT::*func)(ArgT&)) : m_func([func](DerivedT& obj, Base& arg) { // 安全转换:因为ArgT是Base的派生类,这里的静态转换合法 (obj.*func)(static_cast<ArgT&>(arg)); }) {} void invoke(DerivedT& obj, Base& arg) { m_func(obj, arg); } private: Callable m_func; }; // 使用示例 class MyHandler { public: void onBaseEvent(Base& event) {} void onDerivedEvent(Derived& event) {} // 也能兼容! }; int main() { // 适配接受Base的成员函数 Wrapper<MyHandler> wrapper1(&MyHandler::onBaseEvent); // 适配接受Derived的成员函数(因为Derived继承Base) Wrapper<MyHandler> wrapper2(&MyHandler::onDerivedEvent); MyHandler handler; Base baseEvent; Derived derivedEvent; wrapper1.invoke(handler, baseEvent); wrapper2.invoke(handler, derivedEvent); // 这里derivedEvent会被转换为Base&传入lambda }
这个方案的优势是兼容性更强,不仅能处理接受Base的成员函数,还能处理接受Base派生类的成员函数,lambda会自动完成类型转换。
3. 用模板参数推导简化调用(语法糖)
如果你想让调用更简洁,可以写一个辅助函数来推导模板参数,避免手动指定:
template <typename DerivedT, typename BaseT> auto make_wrapper(void (DerivedT::*func)(BaseT&)) { return Wrapper<DerivedT>(func); } // 使用时直接写: auto wrapper = make_wrapper(&MyHandler::onBaseEvent);
这样就不用手动写Wrapper<MyHandler>了,编译器会自动推导模板参数。
核心思路就是:要么让模板类的成员函数指针类型直接匹配目标函数的签名,要么用类型擦除包装成员函数指针,两种方案都能实现你“仅传递成员函数指针给另一类”的目标,你可以根据自己的场景选择最适合的。
内容的提问来源于stack exchange,提问作者Alkenso 'Volodymyr Vashurkin'
相关产品推荐
相关产品推荐

