拥有 X 类,如何将 X 作为模板参数传递给自身?

Posted

技术标签:

【中文标题】拥有 X 类,如何将 X 作为模板参数传递给自身?【英文标题】:Having class X, how to pass X as a template argument to itself? 【发布时间】:2020-10-01 09:28:33 【问题描述】:

我正在尝试实现从 BS 树继承的红黑树。 BST 使用BSNode 作为其节点,而RBT(红黑树)使用RBNode,而RBNode 又继承自BSNode。代码长这样,问题出在代码中:

#include <iostream>

#define _BST BST<K, T, RBNode<K, T>>

// attempt 1
//template <typename K, typename T, typename NODE>
//class BSNode
//
//
//public:
//
//  K key;
//  T value;
//
//  NODE* left;
//  NODE* right;
//
//  // ...
//;

// attempt 2
template <typename K, typename T, template<typename, typename> typename NODE>
class BSNode


public:

    K key;
    T value;

    NODE<K, T>* left;
    NODE<K, T>* right;

    // ...
;

template <typename K, typename T>
class RBNode : public BSNode<K, T, RBNode>


public:

    bool color;

    // ...
;

template <typename K, typename T, typename NODE>
class BST


public:

    NODE* root = nullptr;

    // ...
;

template <typename K, typename T>
class RBT : public _BST

    // ...
;

int main()

    RBT<int, int> rbt;

    // attempt 1
    // recursive and can't be done.
    // BST<int, int, BSNode<int, int, BSNode<int, int, BSNode<int.....

    // attempt 2
    // template arguments list for NODE takes
    // 2 arguments while BSNode takes 3. Not compatible.
    // BST<int, int, BSNode<int, int, BSNode>>

问题:

1 - 如何实现它以便BSTs 只写为BST&lt;int, int&gt;,同时仍然让它接受不同类型的左右节点,例如RBNode

2 - 如何让RBNodeBSNode继承,同时能够创建BST和RBTs?

3 - 在尝试 2 中,在宏 #define _BST BST&lt;K, T, RBNode&lt;K, T&gt;&gt; 中,为什么将其写为 #define _BST BST&lt;K, T, RBNode&gt; 时会出错?在BSNode类中,定义了left和right 作为NODE&lt;K, T&gt;*。这意味着将 NODE 替换为 BSNode&lt;K, T&gt; 将导致 left 和 正确等于BSNode&lt;K, T&gt;&lt;K, T&gt;*。为什么这是正确的方式?

设计是否有问题或是否可以改进?

【问题讨论】:

恐怕你错过了一个额外的间接级别。我认为惯用的方法是使用 CRTP。 @πάνταῥεῖ 你能举个例子吗? 当然:***.com/questions/4173254/… RBNode 应该作为派生自BST 的类类型传递。 标题的问题在于X要么是类,要么是类模板。无关:_BST 是为实现保留的名称。 【参考方案1】:

您需要将NodeTree 与特定的BSNodeRBNodeBSTreeRBTree 类型分开

template <typename K, typename T, template<typename, typename> typename NODE>
struct Node

    K key;
    T value;

    NODE<K, T>* left;
    NODE<K, T>* right;

    // ...
;

template <typename K, typename T>
struct BSNode : Node<K, T, BSNode>
;

template <typename K, typename T>
struct RBNode : Node<K, T, RBNode>

    bool color;

    // ...
;

template <typename NODE>
struct Tree

    NODE* root = nullptr;

    // ...
;

template <typename K, typename T>
struct BSTree : Tree<BSNode<K, T>>

    // ...
;

template <typename K, typename T>
struct RBTree : Tree<RBNode<K, T>>

    // ...
;

除此之外:如果您正在制作 public 的所有内容,那么您最好使用 struct 而不是 class。唯一的区别是默认public

【讨论】:

嗯,我想过这种方式,但我不确定这是否是一个好的解决方案。现在,BSNode 不再是 RBNode 的父级,而是兄弟姐妹,RBNode 可以' 不能代替 BSNode。另外,如果您能够为问题 3 提供答案,我将不胜感激。谢谢! @StackExchange123 它们不是同一类型,所以最好不要混合它们。 #3 是因为您将 NODE 定义为类型,而不是模板。在BST 中没有你写NODE&lt;K, T&gt; 感谢您的回复。我相信 RBNode 只是一个添加了布尔颜色的 BSNode。为什么你说它们不是同一类型? 因为你不应该把RBNode 放在BSTree 您仍然可以编写不关心是否传递 BSTreeRBTree 而没有继承的函数【参考方案2】:

问题在于节点定义。为简单起见,最好使用节点作为树本身,因此删除 BST 和 RBT 类。

节点类可以定义为:

template <typename K, typename T, template<typename, typename> typename Node>
class GenericNode


public:
    K key;
    T value;

    Node<K, T>* left = nullptr;
    Node<K, T>* right = nullptr;

    // common code
;

template <typename K, typename T>
class BSNode : public GenericNode<K, T, BSNode>

    // specific code if exists
;

template <typename K, typename T>
class RBNode : public GenericNode<K, T, RBNode>


public:
    bool color;

    // specific code
;

int main()

    
        BSNode<int, float> n1, n2, n3;
        n1.left = &n2;
        n1.right = &n3;
    

    
        RBNode<int, float> n1, n2, n3;
        n1.left = &n2;
        n1.right = &n3;
        n1.color = true;
    

回复 3. NODE作为BSNode类的模板参数,允许使用指向派生类的指针。否则,BSNode 可用于左右指针,但在许多情况下可能需要对派生类进行静态强制转换(例如 static_cast(left))。这就是使用 NODE 的原因。

【讨论】:

感谢您的回答,但这并不能回答问题 3,另外,树和节点是不同的东西。例如,树将包含根节点,而节点则不包含。 我添加了回复 3. 树节点和树的主要区别是什么? “std::unique_ptr my_tree”可以是完美定义的树。这取决于如何公开节点详细信息。 IMO 节点应该公开方法而不是原始字段。 嗯,例如,在红黑树中,根对它们有特殊的条件。例如,如果根为红色,则可以毫无问题地将其更改为黑色。这仅适用于根。此外,如果树为空,则意味着根为空,但树还不是空的。我希望能够使用树并将其清空并再次填充它而没有问题,为此我需要一个节点(树)的包装器。至于问题3,我不太清楚你的意思。我已经更新了这个问题,这样它就更清楚了。谢谢! 欢迎。关于回复 3,Node struct 只能有 K 和 T 参数以及指向 Node 的左右指针,它可以工作。然后从 Node 派生,您仍然会有指向 Node 的指针(而不是指向后代类的指针)。为了从左右成员访问派生对象(例如 RBNode),需要静态转换。

以上是关于拥有 X 类,如何将 X 作为模板参数传递给自身?的主要内容,如果未能解决你的问题,请参考以下文章

如何将关键字对作为参数传递给函数中的模块?

如何将 lambda 表达式作为参数传递给 c++ 模板

如何将参数传递给不带字符串的函数(Pyspark)

如何将列名作为参数传递给 dplyr 中的函数?

如何将 url 参数传递给视图以供其他类函数和模板使用

将文件x中指定的函数作为命令行参数传递给python中的文件y的最佳方法