CRTP类中如何通过中间结构体B调用派生类的任意成员方法?
原方案可行性判断
原方案不可行,运行后会直接触发无限递归栈溢出,核心问题出在以下两点:
- 你传入结构体
B的模板参数&A<Derived, RetType>::foo是基类A自身的成员函数指针,当callAmethod中通过static_cast<Derived*>(p_A_)调用该指针指向的方法时,实际调用的还是基类A的foo方法,最终形成A::foo() -> B::callAmethod() -> A::foo()的死循环。 - 由于C++的类实例化顺序限制,你在定义基类
A的成员foo_B、bar_B时,派生类还没有被完整定义,无法直接将派生类的成员函数指针作为模板参数传给B,这是语法层面的硬性限制。
可行的修改实现
你可以通过给B增加方法标识的模板参数,编译期确定要调用的派生类方法,就能实现你要的调用链路A.method() -> B.callAmethod() -> 派生类.method(),还可以在B里插入任意中间处理逻辑:
#include <iostream> #include <utility> template <typename Derived, typename RetType> struct A; // 新增方法标签枚举,用来标识要调用的方法 enum class MethodTag { FOO, BAR }; // 调整B的模板参数,用MethodTag替代成员函数指针 template <typename Base, typename Derived, typename RetType, MethodTag tag> struct B { private: Base *p_A_; public: B(Base *p_A) : p_A_(p_A) {} RetType callAmethod() { // 这里可以插入任意你需要的中间处理逻辑 std::cout << "执行B层中间处理" << std::endl; // 编译期根据标签调用派生类对应方法 auto derived_ptr = static_cast<Derived *>(p_A_); if constexpr (tag == MethodTag::FOO) { return derived_ptr->foo(); } else if constexpr (tag == MethodTag::BAR) { return derived_ptr->bar(); } std::unreachable(); } }; template <typename Derived, typename RetType> struct A { private: // 传入对应方法标签实例化B B<A<Derived, RetType>, Derived, RetType, MethodTag::FOO> foo_B; B<A<Derived, RetType>, Derived, RetType, MethodTag::BAR> bar_B; public: RetType foo(); RetType bar(); A() : foo_B(this), bar_B(this) {} }; template <typename Derived, typename RetType> RetType A<Derived, RetType>::foo() { return foo_B.callAmethod(); } template <typename Derived, typename RetType> RetType A<Derived, RetType>::bar() { return bar_B.callAmethod(); } struct A_Derived : public A<A_Derived, int> { int foo() { return 1; } int bar() { return 2; } }; int main() { A_Derived obj; std::cout << obj.foo() << ", " << obj.bar() << std::endl; // 输出: // 执行B层中间处理 // 执行B层中间处理 // 1, 2 }
其他可选方案
如果你需要支持任意数量的派生类方法,不想手动维护枚举,也可以用无状态函数对象作为模板参数传入B,用来调用对应方法,扩展性更强。
内容的提问来源于stack exchange,提问作者Jonas Hjulstad
相关产品推荐
相关产品推荐

