如何用另一个派生class的协变return类型实现一个函数?

How to implement a function with a covariant return type of another derived class?

我的程序有一个抽象 class,BaseNode,有 2 个派生的 classes,ChoiceNode 和 OpponentNode。我想在 BaseNode 中编写一个名为 "returnOpposite" 的纯虚函数,如果从 ChoiceNode 调用它应该 return 一个 OpponentNode,如果从 OpponentNode 调用它应该是一个 ChoiceNode。

在BaseNode.h

class BaseNode {
    protected:
        virtual BaseNode& returnOpposite() = 0;
}

在ChoiceNode.h

#include "BaseNode.h"

class ChoiceNode: public BaseNode {
    OpponentNode& returnOpposite();
}

在OpponentNode.h

#include "BaseNode.h"

class OpponentNode: public BaseNode {
    ChoiceNode& returnOpposite();
}

我的问题是 Opponent/ChoiceNode 需要了解相反的 class,这通常可以通过使用前向声明来解决,但为了让编译器识别相反的 class 与 BaseNode 是协变的,它需要关于 class.

的上下文信息

据我了解,此信息是通过包含相应的头文件提供的。但是,这样做会导致某种循环依赖。 ChoiceNode 需要包含 OpponentNode,它本身需要包含 ChoiceNode,但是有了头部防护,OpponentNode 似乎不知道 ChoiceNode class 声明。

如何解决这个明显的第 22 条军规问题?有没有一种方法可以在不涉及循环依赖的情况下提供有关 class 的上下文信息?

returnOpposite() 方法的所有两个重写应具有相同的 return 类型。

否则你的代码根本无法编译,这个抽象方法virtual Node& returnOpposite() = 0;在派生类中没有实现。

所以你的 类 应该是这样的:

class BaseNode {
    protected:
        virtual BaseNode* returnOpposite() = 0;
}

class ChoiceNode: public BaseNode {
    virtual BaseNode* returnOpposite() override;
}

class OpponentNode: public BaseNode {
    virtual BaseNode* returnOpposite() override;
}

更新(感谢@DaveS 评论):如果这两种类型都是协变的(如您的草图所示),那么以下将正常工作:

class BaseNode {
    protected:
        virtual BaseNode* returnOpposite() = 0;
}

class OpponentNode; // forward declaration

class ChoiceNode: public BaseNode {
    virtual OpponentNode* returnOpposite() override;
}

class OpponentNode: public BaseNode {
    virtual ChoiceNode* returnOpposite() override;
}

请注意,我在这里 return 指向实例的指针而不是引用。

由于您发现的循环依赖问题,您不能使用真正的协变 return 类型,正如您所发现的那样。

但是,您可以使用在引入协变 return 类型之前有效的技术。

首先,我建议将所有 class 中的声明更改为 virtual Node& returnOppositeImpl(),使基础 class 成为纯虚拟的。根据您的 class 层次结构的其余部分,我还会将函数和所有实现声明为 private。然后,您将在每个 class returnOpposite 中声明一个非虚拟保护(或 public)方法,并使用您在上面描述的签名。这些函数定义,因为相互遮蔽,不需要co-variant return类型,它们的实现可以放在各自的.cpp文件中。实现是一个简单的静态或动态转换,调用 returnOppositeImpl 方法。

使用阴影,您将始终在调用站点调用与静态类型相对应的 returnOpposite,因此它将具有正确的引用类型。但是,各个节点 classes 之间的链接的实际知识隐藏在实现中,打破了依赖循环。

所以,综合起来

在BaseNode.h

class BaseNode {
    private:
        virtual Node& returnOppositeImpl() = 0;
    protected: // Or public:
         Node& returnOpposite() {
            return returnOppositeImpl();
         }
}

在ChoiceNode.h

#include "BaseNode.h"
class OpponentNode;
class ChoiceNode: public BaseNode {
    private: 
        virtual Node& returnOppositeImpl();
    protected: // or public:
        OpponentNode& returnOpposite();
}

在ChoiseNode.cpp

#include "ChoiseNode.h"
#include "OpponentNode.h"
OpponentNode& ChoiseNode::returnOpposite()
{
  // You can use static cast here, if Node doesn't have virtual
  // members, and/or if you can guarantee further subclasses
  // will properly always return an OpponentNode reference.

  return dynamic_cast<OpponenentNode&>(returnOppositeImpl());
}

OpponentNode.h 和 OpponentNode.cpp 类似于 ChoiseNode 实现