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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 05:45:04