如何用另一个派生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 实现
我的程序有一个抽象 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 实现