使用CRTP和虚拟继承

时间:2014-03-26 12:13:42

标签: c++ crtp virtual-inheritance

我有一个节点层次结构,其中"菱形"可能发生了。

每个节点都必须是可克隆的,但我不想将克隆方法写入每个节点。所以我使用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"; }

}; 

第一个问题:

当&#34;协方差与虚拟继承&#34;一起使用时,VS中知道BUG。 有没有办法克服这个错误,并且仍然有协变类型是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的完整工作示例。

2 个答案:

答案 0 :(得分:3)

每个虚函数必须在每个派生类中具有唯一的最终覆盖。这与名称查找无关(要求是函数,而不是名称),因此using无关紧要。

使用多基类的节点类模板:

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

至于协变回报,如果方便的话,它们是完全没必要的。您始终可以将每个虚拟功能拆分为私有虚拟和公共非虚拟虚拟。如果你想返回协变智能指针,这很方便,而常规的协变返回机制根本不支持它。

答案 1 :(得分:3)

首先,您应该非常小心地将虚拟继承与虚拟基础内的成员一起使用(查看https://stackoverflow.com/a/1193516/1918154,&#34; Effective C ++&#34;,第20项:&#34;避免数据公共界面中的成员&#34;和http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.8)。你的node获得了一个未使用的fill指针,但看起来你需要它。

当您在public virtual的基类中移动关联关系(publicNodeWrap)时,您的问题就可以解决了。

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(它是虚拟基础)不同,让NodeBNodeC具有成员变量和非平凡构造函数是可以的。