如何在不进行动态调用的情况下确定子节点的类型

时间:2019-06-15 02:12:01

标签: c++

我有一个像这样的节点树结构:

struct Node {};
struct ChildNode : Node{};

我将这样做:

Node* n = new ChildNode;

稍后,我需要知道节点类型:

if (IsChildNode(n)) {
    ChildNode* c = static_cast<ChildNode*>(n);
    // ...
}

通常,我通过向Node基类添加虚拟Id()方法并在派生类中重写此方法来实现此目的。

在我当前的情况下,这是唯一需要虚拟的方法,因此我试图查看是否有一种避免虚拟方法的方法,同时使Id成为编译时值(不是Node类中的非常量成员变量)。

有没有一种方法可以找出ChildNode的类型:

  • 使用dynamic_cast
  • 不使用虚函数
  • 不在Node类中设置运行时变量。

我在Visual Studio 2019上使用C ++ 17,

2 个答案:

答案 0 :(得分:1)

广义上,您有一个指针和一个指向的对象:

Node*           ChildNode
+------+        +------------------
| XXXX | -----> | ... some data ... 
+------+        +------------------

如果您需要在运行时确定指向对象的类型,而无需选择静态类型系统(包括模板),则这两个地方是 only 的来源信息。

通过拒绝使用dynamic_castvirtual函数,您已经消除了C ++内置的用于动态类型信息的机制。通过拒绝使用成员变量,您还消除了以类似方式手动实现类型检查的可能性。除非不同的Node类型在对象中存储了其他唯一值,否则无法使用指向的对象的值来确定其类型。

但是,指针本身可以成为信息的来源。例如,在标准用法下,指针可以通过与null比较来指示它确实指向了一个对象。我在评论中开玩笑地指出,可以为每种Node类型使用唯一的分配器,这些分配器可以标识它们自己的指针。但在有限制的情况下,它是一个 选项。这必须通过以下方式实现:

  • 相邻分配的值(但该值几乎与成员变量相同)
  • 某种查找(可能非常慢)
  • 一个tagged pointer(非常不标准且易碎)

这些选项中的任何一个都没有任何好处。 RTTI和成员变量都是解决问题的非常常用的方法。我不鼓励您使用上面列出的机制中的 any

我无法使用规范回答,因为不可能证明是否定的。但是,我希望我已经从概念上证明了这种选择很少。

答案 1 :(得分:0)

我正在查看LLVM如何进行投射。几乎可以肯定,它们的实现将是最快的。以下是与此有关的一些文件:

https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html

http://llvm.org/docs/ProgrammersManual.html#the-isa-cast-and-dyn-cast-templates

https://llvm.org/doxygen/Casting_8h_source.html

基本上,他们具有这样的设置:

enum class Kind {
    ChildNode
};

struct Node {
    Node(Kind k) : kind(k) {}
    const Kind kind;
};

struct ChildNode : Node {
    ChildNode() : Node(Kind::ChildNode) {}

    static bool classof(const Node* n) { return n->kind == Kind::ChildNode;}
};

然后,他们做了一些模板欺骗来定义泛型函数:

bool isa<TypeToCheckFor, CurrentType>(const CurrentType& t);

可以叫哪个来检查类型。

尽管此版本不符合我的这一要点:

* Without setting a runtime variable in the Node class.

至少,它使用const基本类型成员,没有虚拟调用和const参数。这将是设置的最佳选择,因为编译器可能必须对这些检查执行尽可能多的优化。