У меня есть иерархия узлов, где может возникнуть «алмаз».
Каждый узел должен быть клонируемым, но я не хочу писать метод клонирования для каждого узла. Поэтому я использую 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));
}
};
работает следующим образом:
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"; }
};
Первый вопрос:
Известно ОШИБКА в VS, когда "ковариация используется с виртуальным наследованием". Есть ли способ преодолеть ошибку, и все еще иметь ковариантные типы - это метод clone
?
Я изменил тип возвращаемого значения на Node
вместо Base
. Я могу жить с этим, но я хотел бы иметь Base
в качестве возвращаемого типа
Второй вопрос: возникла проблема, когда в игру вступает множественное наследование. Я создал новую оболочку, которая наследует virtually
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));
}
};
и теперь строим ромбовидную структуру:
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"; }
};
и проблемный алмазный узел:
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.