c++ - 使用具有虚拟继承的 CRTP

标签 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"; }

}; 

第一个问题:

有知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 virtualpublic)时,您的问题就可以解决。

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 具有成员变量和非平凡的构造函数。

关于c++ - 使用具有虚拟继承的 CRTP,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22660692/

相关文章:

C++ 宏文本操作

c++ - 在 C++ 中使用静态类成员

c++ - CRTP : returning a reference to the derived class?

c++ - 使用 CRTP 进行模拟类

c++ - 虚继承构造函数顺序

c++ - SELinux:重定位后无法恢复段保护:权限被拒绝

c++ - 我认为这是 C++11 标准中的一个(小)缺陷

c++ - CRTP 静态多态性 : Using the Base Class to Call Derived Methods

c++ - C++接口(interface)多重继承中的歧义

c++ - 在未完成的类层次结构中的 "final"类上使用虚拟继承