Using CRTP with virtual inheritance

I have a hierarchy of nodes where a “diamond” can occur.

Each node must be cloned, but I do not want to write a clone method for each node. Therefore, I use CRTP.

class Node
{
public:
    Node(){}
    Node(Fill*) { }

    virtual ~Node() {}
    virtual Node * clone() const = 0;

    virtual void id() { std::cout << "Node\n"; }
};

//====================================================================

template <typename Base, typename Derived>
class NodeWrap : public Base
{
public:

    NodeWrap() { } 
    NodeWrap(Fill * arg1) : Base(arg1) { }

    virtual Node *clone() const
    {
        return new Derived(static_cast<Derived const &>(*this));
    }
};

works as follows:

class NodeA : public NodeWrap<Node, NodeA>
{
public:
    typedef NodeWrap<Node, NodeA> BaseClass;

    NodeA() { }
    NodeA(Fill * f) : BaseClass(f) { }

    virtual void id() { std::cout << "NodeA\n"; }

}; 

First question:

There is knowing BUG in VS when “covariance is used with virtual inheritance”. Is there a way to overcome the error and still have covariant types of clonemethod?

I changed the return type as Nodeinstead Base. I can live with it, but I would like to have Basea return type

Second question: The problem arose when multiple inheritance appeared. I created a new shell that inheritsvirtually

template <typename Base, typename Derived>
class NodeWrapVirtual : public virtual Base
{
public:

    NodeWrapVirtual() { }
    NodeWrapVirtual(Fill * arg1) : Base(arg1) { }

    virtual Node *clone() const
    {
        return new Derived(static_cast<Derived const &>(*this));
    }
};

and now build the diamond structure:

class NodeB : public NodeWrapVirtual<Node, NodeB>
{
public:
typedef NodeWrapVirtual<Node, NodeB> BaseClass;

NodeB() { }
NodeB(Fill * f) : BaseClass(f) { }

virtual void id() { std::cout << "NodeB\n"; }
};

//====================================================================

class NodeC : public NodeWrapVirtual<Node, NodeC>
{
public:
    typedef NodeWrapVirtual<Node, NodeC> BaseClass;

    using BaseClass::clone;

    NodeC() { }
    NodeC(Fill * f) : BaseClass(f) { }

    virtual void id() { std::cout << "NodeC\n"; }
};

node:

class NodeD : public NodeWrap<NodeB, NodeD>,
              public NodeWrap<NodeC, NodeD>
{
public:

    typedef NodeWrap<NodeB, NodeD>  BaseClassB;
    typedef NodeWrap<NodeC, NodeD>  BaseClassC;

    NodeD() { }
    NodeD(Fill * f) : BaseClassB(f), BaseClassC(f) { }

    using BaseClassB::clone;  // (1)
    virtual NodeD *clone() const { return new NodeD(*this); }       // (2)

    virtual void id() { std::cout << "NodeD\n"; }
};

2 . ( (1) (2))

, , clone ( ). , clone , (1), . .

(2), .

(2)?

- ideone.

+4
2

( fooobar.com/questions/637520/..., " ++", 20: " " http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.8). node fill, , , .

, (public virtual public) NodeWrap.

template <typename Base>
class  InheritVirtual
    : public virtual Base
{};

template <typename... Bases>
class InheritBases
    : public Bases...
{
    virtual Node* clone() const = 0;
    virtual void id() const = 0;
};

class NodeB : public NodeWrap<InheritVirtual<Node>, NodeB>
{ 
   //...
};


class NodeC : public NodeWrap<InheritVirtual<Node>, NodeB>
{ 
   //...
};

class NodeD : public NodeWrap<InheritBases<NodeB,NodeC>, NodeD>
{ 
   //...
};

.

InheritBases , (Dominance ).

, , - . node ( ), NodeB NodeC - .

+3

. ( , ), using .

node :

 template <class Derived, class Base1, class Base2>
 class node2 : //  etc
 // or use a variadic template if you have more than two bases

, , . , . , , .

+3

Source: https://habr.com/ru/post/1533520/


All Articles