对于迭代器和类似情况,应如何处理“无效的协变量返回类型”

时间:2019-06-28 16:15:06

标签: c++ inheritance inner-classes covariance

我正在尝试创建一个继承自Tree的二进制树类BST和一个二进制搜索树类Tree以及嵌套的iterator嵌套类每个,其中BST::iterator继承自Tree::iterator

现在,问题在于某些树的函数必须返回其自身类的iterator,例如begin()end()search(T)等。由于Tree::begin()BST::begin()具有“无效的协变量返回类型”,因此似乎无法编译。

研究了上述主题之后,我了解了导致编译器抱怨的原因,但我不明白为什么。例如,在这种情况下,Tree应该返回类型为Tree::iterator的对象,而BST应该返回类型为BST::iterator的对象似乎是合乎逻辑的。

下面是一些代码,应说明我正在处理的内容。

Tree.h

template <class T>
class Tree {
  protected:
    class Node {
        friend Tree;
        T value;
        Node* left;
        Node* right;
    };

    Node* root;

  public:
    class iterator {
        friend Tree;
        Node* node;
      public:
        // operators and the like...
    };

    virtual iterator begin() const;
    virtual iterator end() const;
    virtual iterator search(const T& value) const;
};

BST.h

#include "Tree.h"

template <class T>
class BST : public Tree<T> {
  protected:
    class Node : public Tree<T>::Node {
        friend BST;
    };

    using Tree<T>::root;

  public:
    class iterator : public Tree<T>::iterator {
        friend BST;
    };

    using Tree<T>::begin;
    using Tree<T>::end;
    virtual iterator search(const T& value) const override;

};

对我来说很明显,在这种情况下,搜索试图返回一个BST<T>::iterator,这是不允许的,因为它覆盖了一个返回Tree<T>::iterator的函数,但是对我来说似乎合乎逻辑应该允许这样做,我不确定应该如何做。

此外,当BST<T>继承begin()end()时,我假设它继承了它们,使得它们返回Tree<T>::iterators,尽管它们实际上应该返回{{1} }。

我到底想念什么,应该如何实现?

1 个答案:

答案 0 :(得分:1)

多态只能通过指针/引用来完成,允许返回对象的协方差会引入切片。

我们不能将协方差关系添加到“无关”类型作为智能指针。 (而Base*Derived*可以协变,std::unique_ptr<Base>std::unique_ptr<Derived>不能:/)

协方差可以通过指针/引用来完成。

解决这些限制的一种方法是拥有2种方法,一种具有支持的协方差的虚拟方法,以及一种使用虚拟的方法模拟协方差的常规方法:

template <typename T>
class Tree {
    // ...
    // Assuming iterator might be constructed with node.
    typename Tree<T>::iterator begin() const { return {node_begin()/*, ...*/}; }
    typename Tree<T>::iterator end() const   { return {node_begin()/*, ...*/}; }

protected:
    virtual typename Tree<T>::node* node_begin() const;
    virtual typename Tree<T>::node* node_end() const;
};

template <class T>
class BST : public Tree<T> {
    // ...

    typename BST<T>::iterator begin() const { return {node_begin()/*, ...*/}; }
    typename BST<T>::iterator end() const   { return {node_begin()/*, ...*/}; }

protected:
    typename BST<T>::node* node_begin() const override; // covariance on pointer
    typename BST<T>::node* node_end() const override;   // covariance on pointer
};