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

多层继承与Mixin模式下CRTP实现的问题与优化咨询

多层继承与Mixin模式下CRTP实现的问题与优化咨询

我实现了一个带多层继承的CRTP,目前看起来运行效果不错:外部调用任何方法时,都会调用最派生类的实现;而且就算某个方法的实现里调用了其他方法,不管调用方在继承链的哪一层,最终都会调用这些方法的最派生实现。这个效果是通过每个类里的LD& getImpl() { ... }实现,以及从最底层派生类向上传递的LD模板参数来达成的。

struct Base {};

template<typename LD>
struct CommonImpl : public Base {
    LD& getImpl() { return *static_cast<LD*>(this); }

    void foo() { std::cout << "Common::foo" << std::endl; }
    void bar() { std::cout << "Common::bar" << std::endl; }
    void baz() { std::cout << "Common::baz" << std::endl; }

    void some() {
        std::cout << "--- Common::some" << std::endl;
        getImpl().foo();
        getImpl().bar();
        getImpl().baz();
    }

    void other() {
        std::cout << "--- Common::other" << std::endl;
        getImpl().foo();
        getImpl().bar();
        getImpl().baz();
    }
};

template<typename LD>
struct SublevelImpl : public CommonImpl<LD> {
    LD& getImpl() { return *static_cast<LD*>(this); }

    void baz() { std::cout << "Sublevel::baz" << std::endl; }
};

template<typename T>
struct AImpl : public CommonImpl<T> {
    void foo() { std::cout << "A::foo" << std::endl; }
    void bar() { std::cout << "A::bar" << std::endl; }
};

struct A : AImpl<A> {};

template<typename T>
struct BImpl : public SublevelImpl<T> {
    T& getImpl() { return *static_cast<T*>(this); }
    
    void foo() { std::cout << "B::foo" << std::endl; }

    void other() {
        std::cout << "--- B::other" << std::endl;
        getImpl().foo();
        getImpl().bar();
        getImpl().baz();
    }
};

struct B : BImpl<B> {};

int main() {
    auto a = A();
    a.some();
    a.other();

    // --- Common::some
    // A::foo
    // A::bar
    // Common::baz
    // --- Common::other
    // A::foo
    // A::bar
    // Common::baz

    auto b = B();
    b.some();
    b.other();

    // --- Common::some
    // B::foo
    // Common::bar
    // Sublevel::baz
    // --- B::other
    // B::foo
    // Common::bar
    // Sublevel::baz
}

就我目前的测试来看,这个实现除了是静态分发之外,效果和普通的虚函数多态完全一致。而且它还允许灵活选择把哪些Impl类插入到继承链中——比如上面的例子里,CommonImpl <- AImpl,而CommonImpl <- SublevelImpl <- BImpl。不过我想问问,这个实现有没有什么潜在的问题?


我现在碰到一个问题:如果有很多独立的Impl类,我没法轻松地组合出最底层派生类需要的实现,因为每个Impl的父类都是固定的。本质上我想把这些多层继承的CRTP Impl当成Mixin来自由组合。我目前尝试的写法大概是这样:

struct Base {};

template<typename LD>
struct Top : public Base {};

template<template<class> class B, typename LD>
struct XImpl : public B<LD> {
    LD& getImpl() { return *static_cast<LD*>(this); }

    void foo() { std::cout << "X:foo" << std::endl; }
    void bar() { std::cout << "X:bar" << std::endl; }
    void baz() { std::cout << "X:baz" << std::endl; }

    void some() {
        std::cout << "--- X::some" << std::endl;
        getImpl().foo();
        getImpl().bar();
        getImpl().baz();
    }

    void other() {
        std::cout << "--- X::other" << std::endl;
        getImpl().foo();
        getImpl().bar();
        getImpl().baz();
    }
};

template<typename LD>
struct XTop : public XImpl<Top, LD> {};

template<template<class> class B, typename LD>
struct YImpl : public B<LD> {
    LD& getImpl() { return *static_cast<LD*>(this); }

    void bar() { std::cout << "Y::bar" << std::endl; }

    void other() {
        std::cout << "--- Y::other" << std::endl;
        getImpl().foo();
        getImpl().bar();
        getImpl().baz();
    }
};

template<typename LD>
struct YTop : public YImpl<Top, LD> {};

template<template<class> class B, typename LD>
struct ZImpl : public B<LD> {
    LD& getImpl() { return *static_cast<LD*>(this); }

    void baz() { std::cout << "Z::baz" << std::endl; }
};

template<typename LD>
struct ZTop : public ZImpl<Top, LD> {};

// ----------

template<typename LD>
struct Y_X_Mix : public YImpl<XTop, LD> {};

template<typename LD>
struct Z_Y_X_Mix : public ZImpl<Y_X_Mix, LD> {};

template<typename LD>
struct Z_X_Mix : public ZImpl<XTop, LD> {};

struct MixA : Z_Y_X_Mix<MixA> {};

struct MixB : Z_X_Mix<MixB> {};

int main() {
    auto a = MixA();
    a.some();
    a.other();

    // --- X::some
    // X:foo
    // Y::bar
    // Z::baz
    // --- Y::other
    // X:foo
    // Y::bar
    // Z::baz

    auto b = MixB();
    b.some();
    b.other();

    // --- X::some
    // X:foo
    // X:bar
    // Z::baz
    // --- X::other
    // X:foo
    // X:bar
    // Z::baz
}

这个实现比之前好一些,但灵活性还是不够——每加一个Impl到最终的组合里,我都得新建一个中间的Mix类,这会产生一大堆混乱的Impl组合变体。我理想中的写法是这样的:

template<typename LD>
struct MixAImpl : public ZImpl<YImpl<XImpl<LD>, LD>, LD> {};

template<typename LD>
struct MixBImpl : public ZImpl<XImpl<LD>, LD> {};

struct MixA : MixAImpl<MixA> {};

struct MixB : MixBImpl<MixB> {};

甚至更好的是,能直接把要组合的Impl作为模板参数包传进去:

template<typename LD>
struct MixAImpl : public Mixer<ZImpl, YImpl, XImpl, LD> {};

template<typename LD>
struct MixBImpl : public Mixer<ZImpl, XImpl, LD> {};

这种写法可行吗?另外,我的整个实现还有什么其他问题或者可以优化的地方吗?

备注:内容来源于stack exchange,提问作者Ramon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 18:08:04