多层继承与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
相关产品推荐
相关产品推荐

