Multilevel inheritance in c++ (CRTP)

余生颓废 提交于 2019-12-02 11:46:59

(Copied from an earlier comment.)

Perspective #1

CRTP is meant to provide non-dynamic behavior. If the value of "numAttacks" vary with each derived class, this is not a "non-dynamic" situation. A counter-example would be to put a non-static non-virtual method int numAttacks() { return 3; } in a derived class, and then in the CRTP base class add some methods (the attack logic that is shared across all derived classes), which can then call the numAttacks() method on its derived class, without incurring a virtual function call.

Example:

struct Monster
{
    virtual void attack() = 0;
    virtual int getNumAttacks() const = 0;
};

template <struct MONSTER>
struct AttackLogic : virtual public Monster
{
    virtual void attack() override
    {
        /* allowed to call MONSTER::getNumAttacks(), renamed to avoid confusion. */

        int numAttacks = static_cast<MONSTER*>(this).getNumAttacks();

        /* Use the value in attack calculations. */
    }
};

struct Unicorn 
    : virtual public Monster
    , virtual public AttackLogic<Unicorn>
{
    virtual int getNumAttacks() const override
    {
        return 42; // Unicorn is awesome
    }
};

Disclaimer: Code only meant to explain my suggestion. Not intended for practical use. Not tested with compiler. My knowledge of virutal inheritance is weak, so there may be mistakes or broken guidelines in the sample code above.


Your current inheritance chain is: (base at top)

  • Monster
    • Dragon
      • MonsterImplement<Dragon, WhiteDragon>
        • WhiteDragon

Monster defines:

  • virtual finalizeMonster() // abstract
  • virtual attack() // abstract

Dragon defines:

  • virtual attack() // concrete, overrides Monster.attack()

MonsterImplement<...> defines:

  • virtual attack() // concrete, overrides Dragon.attack() and Monster.attack()

WhiteDragon defines:

  • (no new virtual methods defined)

It is very clear that "after fixing the bug", that MonsterImplement.attack() will be called, because it is a subclass of Dragon and therefore overrides it.

In general it only says that the current inheritance hierarchy is badly designed, and that nobody would be able to fix it.


Perspective #2

Injecting a static int through CRTP pattern is rarely worth the effort. CRTP is more suitable for injecting a set of non-static, non-virtual methods ("boilerplate") in a way that will not be overridden, that saves every derived class from re-implementing the same "boilerplate".

At the minimum, convert the static int numAttacks into a virtual function

virtual int numAttacks() const { throw std::exception(); }

or

virtual int numAttacks() const = 0;  // abstract

and then provide a concrete implementation in WhiteDragon to return 3.

struct WhiteDragon : ... 
{   ... 
    virtual int numAttacks() const override { return 3; } 
    ... 
};

template <class MONSTER, int NUM>
struct MonsterInt: virtual public Monster {
    static int numAttacks;
   };

What's the purpose of this class? It seems like all it does is give a class a number of attacks, in which case it doesn't really make sense to derive from monster.

template <int NUM>
struct MonsterInt {
    static int numAttacks;
   };

That 'fixes' the program I think, but it's hard to really say because intention is hard to derive from your code.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!