简体   繁体   中英

C++ nested class inheriting from nested class, type issue

I met a problem when trying to implement a red-black tree class. There's a binary tree class I have built previously, so I inherited my red-black tree from it.

the binary tree declaration is like this:

template <typename KeyType, typename DataType>
class BTree{
protected:
    struct BTreeNode{
        KeyType key;
        DataType data;
        BTreeNode *left;
        BTreeNode *right;
        BTreeNode *parent;
        BTreeNode(const KeyType &Key, const DataType &Data);
    };
    BTreeNode *root;
    void clear(BTreeNode *root);

public:
    BTree();
    virtual ~BTree();

    virtual bool insert(const KeyType &Key, const DataType &Data);
    virtual bool erase(const KeyType &key);
    DataType &getFirst(const KeyType &Key);
    /**** some other methods ****/

}; // class bTree

and the red-black tree is like this:

template <typename KeyType, typename DataType>
class RBTree : public BTree<KeyType, DataType>{
protected:
    struct RBTreeNode : BTree<KeyType, DataType>::BTreeNode{
        enum RB{red, black} rb;
        RBTreeNode(const KeyType &Key, const DataType &Data, RB Rb=red);
    };

private:
    void leftRotate(RBTreeNode *x);
    void rightRotate(RBTreeNode *x);

public:
    /**** some other methods ****/
}; // class RBTree

Here comes the question, when I implement RBTree::leftRotate() , I must write something like RBTreeNode *y = x->right and y=y->left , but x->right and y->left are BTreeNode * , and cannot be converted to RBTreeNode * automatically.

I don't want my code full of things like static_cast<RBTreeNode *>(x) , also, I don't want to redefine RBTreeNode or RBTree class, which means I have to rewrite every method in BTree like getFirst() I could have used directly.

So how should I construct RBTree? Must I re-write the whole RBTree class? Thanks a lot.

You can use this trick:

template <typename D>
struct Base{
    D *root;
};

struct Derived : Base<Derived>
{
};

int main()
{
    Derived x;
    x.root = 0;
    return 0;
}

I simplified a lot your example and removed all the additional template parameters which are unnnecessary to illustrate the point. Note that the Base class contains a pointer to the Derived class, which is passed to it as a template argument.

In your case, add one template parameter D to your base class, define the base class pointers to be of type D*, when you define the derived class, pass as template parameter your derived class itself. Eg

template <typename KeyType, typename DataType>
class RBTree : public BTree<KeyType, DataType, RBTree<KeyType,DataType> >
{...}

There are lot of variations to this theme, for instance, in your case, your template argument could be a template of template argument.

This trick works well when in the base class you use pointers to the derived class. If in the base class you had an actual instance of the derived class, this usually would not work unless the derived class had no data members.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM