简体   繁体   English

C ++(CRTP)中的多级继承

[英]Multilevel inheritance in c++ (CRTP)

Please help me solve this problem. 请帮我解决这个问题。 WhiteDragon is to call Dragon::attacks() instead of MonsterImplement::attacks() , and there is ambiguity error here. WhiteDragon将调用Dragon::attacks()而不是WhiteDragon MonsterImplement::attacks() ,并且此处存在歧义错误。 If I change Dragon to be derived from MonsterImplement, then the line std::cout << monster->numAttacks << std::endl; 如果我将Dragon更改为从MonsterImplement派生,则行std::cout << monster->numAttacks << std::endl; won't compile because Dragon has no numAttacks data member (nor should it, because different types of Dragons are to have different values). 由于Dragon没有numAttacks数据成员而不会编译(也不应这样做,因为不同类型的Dragon具有不同的值)。 So I need WhiteDragon to call Dragon::attacks() and to call finalizeMonster() during its instantiation. 因此,我需要WhiteDragon在其实例化期间调用Dragon::attacks()并调用finalizeMonster() If I make Dragon virtual derived class of Monster, WhiteDragon calls up MonsterImplement::attacks() instead. 如果我将Dragon虚拟化为Monster的派生类,则WhiteDragon会调用MonsterImplement :: attacks()。

#include <iostream>

struct Monster {
    virtual void finalizeMonster() {}
    virtual void attack() {}
};

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

template <class MONSTER, int NUM>
int MonsterInt<MONSTER, NUM>::numAttacks = NUM;

template <class BASE, class MONSTER>
struct MonsterImplement: virtual public BASE {
    MonsterImplement() {finalizeMonster();}
    virtual void finalizeMonster() override;
    virtual void attack() override {std::cout << "MonsterImplement::attack()" << std::endl;}
};

struct Dragon: public Monster {  // or Dragon: public MonsterImplement<Monster, Dragon> ?
// but then Dragon will also call the MonsterImplement constructor (when it has no numAttacks member)
    virtual void attack() override {std::cout << "Dragon::attack()" << std::endl;}
};

struct WhiteDragon: public MonsterInt<WhiteDragon, 3>, 
    public MonsterImplement<Dragon, WhiteDragon> {
    WhiteDragon(): MonsterImplement<Dragon, WhiteDragon>() {}
};

template <class BASE, class MONSTER>
inline void MonsterImplement<BASE, MONSTER>::finalizeMonster() {
    MONSTER* monster = static_cast<MONSTER*> (this);
    std::cout << monster->numAttacks << std::endl;
}

int main() {
    WhiteDragon wd;
    wd.attack();
}

(Copied from an earlier comment.) (摘自先前的评论。)

Perspective #1 观点1

CRTP is meant to provide non-dynamic behavior. CRTP旨在提供非动态行为。 If the value of "numAttacks" vary with each derived class, this is not a "non-dynamic" situation. 如果“ numAttacks”的值随每个派生类而变化,则不是“非动态”情况。 A counter-example would be to put a non-static non-virtual method int numAttacks() { return 3; } 一个反例是将一个非静态非虚拟方法int numAttacks() { return 3; } 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. int numAttacks() { return 3; }在派生类,然后在CRTP基类添加一些方法(即在所有派生类共享攻击逻辑),其然后可以调用numAttacks()在它的派生类的方法,而不会产生一个虚拟函数调用。

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: Monster定义:

  • virtual finalizeMonster() // abstract 虚拟finalizeMonster() //抽象
  • virtual attack() // abstract 虚拟attack() //抽象

Dragon defines: Dragon定义:

  • virtual attack() // concrete, overrides Monster.attack() 虚拟attack() //具体,覆盖Monster.attack()

MonsterImplement<...> defines: MonsterImplement<...>定义:

  • virtual attack() // concrete, overrides Dragon.attack() and Monster.attack() 虚拟attack() //具体,覆盖Dragon.attack()和Monster.attack()

WhiteDragon defines: WhiteDragon定义:

  • (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. 很明显,“修复错误之后”, MonsterImplement.attack()将被调用,因为它是Dragon的子类,因此将其覆盖。

In general it only says that the current inheritance hierarchy is badly designed, and that nobody would be able to fix it. 通常,它仅表示当前继承层次结构设计不当,没有人能够修复它。


Perspective #2 观点2

Injecting a static int through CRTP pattern is rarely worth the effort. 通过CRTP模式注入静态int很少值得付出。 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". CRTP更适合于以不会被覆盖的方式注入一组非静态,非虚拟方法(“样板”),从而避免每个派生类重新实现相同的“样板”。

At the minimum, convert the static int numAttacks into a virtual function 至少将静态int numAttacks转换为虚函数

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. 然后在WhiteDragon提供具体的实现以返回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. 我认为这可以“修复”程序,但是很难说出来,因为很难从代码中得出意图。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM