我有一个节点层次结构,其中可能出现“菱形”。
每个节点都必须是可克隆的,但我不想将克隆方法写入每个节点。所以我使用 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"; }
};
第一个问题:
有知BUG在 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) 行?
HERE是 ideone 上的完整工作示例。
最佳答案
首先,您应该非常小心地使用虚拟基类内部成员的虚拟继承(查看 https://stackoverflow.com/a/1193516/1918154 ,“Effective C++”,第 20 项:“避免公共(public)接口(interface)中的数据成员”和 http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.8)。您的 node
获得一个指向未使用的 fill
的指针,但看起来您在某处需要它。
当您在 NodeWrap
的基类中移动继承关系(public virtual
和 public
)时,您的问题就可以解决。
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>
{
//...
};
正在运行 Example .
InheritBases
中的纯虚方法是必需的,因为所谓的支配规则 (Dominance in virtual inheritance)。
要解决的问题是如何在多个基数的情况下将参数传递给正确的构造函数。与 Node
(这是一个虚拟基础)不同,可以让 NodeB
和 NodeC
具有成员变量和非平凡的构造函数。
关于c++ - 使用具有虚拟继承的 CRTP,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22660692/