如何在c ++中的派生类中实现迭代器?

时间:2016-07-20 16:00:57

标签: c++ inheritance iterator

class List{

    public:
        class ListIterator{

            public:

        };
        typedef ListIterator iterator;
        virtual iterator begin() = 0;
};
class ArrayList:public List{
    public:
        class ArrayListIterator{

            public:

        };
        typedef ArrayListIterator iterator;
        iterator begin(){

        }
};
class LinkedList:public List{

    public:
        class LinkedListIterator{

            public:

        };
        typedef LinkedListIterator iterator;
        iterator begin(){

        }
};

我想以这种方式实现迭代器。但编译器显示错误

[Error] invalid covariant return type for 'virtual ArrayList::iterator ArrayList::begin()'
[Error] overriding 'virtual List::iterator List::begin()'

LinkedList相同。

当我搜索堆栈溢出同样的问题时,我得到了这个解决方案

Iterator for custom container with derived classes 我得到两个解决方案

1 . implement iterator without using runtime polymorphism
2 . implement seperator iterator class.

但我想知道是否有任何可能的方式使迭代器成为ArrayList,LinkedList和List的内部类?

2 个答案:

答案 0 :(得分:-1)

您需要ArrayListIterator从ListIterator派生。

派生类中begin返回的类型不是从基类List类中的begin返回的类型派生的。

这就是为什么错误说它们不是协变的。

答案 1 :(得分:-1)

你需要抛弃基于继承的多态性。标准库和for(:)循环中的C ++迭代假定基于值的迭代器,基于继承的多态不适用于基于值的迭代器。

您应该使用基于类型擦除的多态性,而不是基于继承的多态性。这需要一些样板,因为在C ++中发现需要抽象地处理可迭代范围的需求并不常见,并且通常会造成严重的性能损失。

但我会告诉你如何。

基于类型擦除的多态性类似于std::function

类型擦除多态的一种具体方法:

您可以确定要支持的界面。将此称为"类型擦除目标"。您的类型擦除目标是具有virtual=0的类,而是您要支持的一组函数,方法和操作,以及它们的签名和描述各自做了什么。

然后编写一个实现的值类,该接口(同样,没有继承)包含一个pImpl(参见pImpl模式),它将其操作分派给( pImpl不需要匹配相同的接口,它只需要你可以实现操作的原语。这里最小的是值得的。)

pImpl类型 具有virtual方法和=0抽象方法。

然后编写一个构造函数或工厂函数,它接受一个支持你想要的接口的对象,并生成pImpl的具体实例,然后在其周围包装值类。

假设类型擦除目标是"打印到流"。

我的类型擦除目标是std::ostream& << foo可以工作,并打印东西。

struct printable_view {
  // dispatch to pimpl:
  friend std::ostream& operator<<( std::ostream& o, printable_view const& p ) {
    p->print(o);
    return o;
  }
  // pimpl:
private:
  struct printable_view_impl {
    virtual ~printable_view_impl() {}
    virtual void print(std::ostream& o) = 0;
  };
  std::unique_ptr<printable_view_impl> pImpl;

private:
  template<class T>
  struct printer:printable_view_impl {
    printer( T const* p ):ptr(p) {}
    T const* ptr; // just a view, no ownership
    virtual void print( std::ostream& o ) final override {
      o << *ptr;
    }
  };
public:
  // create a pimpl:
  printable_view(printable_view&&)=default;
  printable_view(printable_view const&)=delete;
  printable_view()=delete;
  template<class T>
  printable_view( T const& t ):
    pImpl( std::make_unique<printer<T>>( std::addressof(t) ) )
  {}
};

可能有错别字,但你明白了。

如果要查找示例实现,

Boost has a generic iterator and range您的基类可以返回。

使用基于类型擦除的迭代有很大的性能提升:你最终会得到与C#/ Java代码一样慢的C ++代码。

在你的情况下,你需要按照标准的要求, iterator要求的每个事物(复制,增加,移动,取消引用等),并像我复制一样删除它。在这种情况下,它不是一个视图,因此您的impl impl可能会包含T而不是T*

这是一个非常简单的玩具for(:),它支持玩具伪迭代器,允许你的List基地在for(:)循环中使用。

template<class T>
struct any_iterator_sorta {
  T operator*()const { return pImpl->get(); }
  void operator++() { pImpl->next(); }
  any_iterator_sorta(any_iterator_sorta const& o):
    any_iterator_sorta( o.pImpl?any_iterator_sorta(o.pImpl->clone()):any_iterator_sorta() )
  {}
  friend bool operator==(any_iterator_sorta const& lhs, any_iterator_sorta const& rhs ) {
    if (!lhs.pImpl || ! rhs.pImpl)
      return lhs.pImpl == rhs.pImpl;
    return lhs.pImpl->equals( *rhs.pImpl );
  }
  friend bool operator!=(any_iterator_sorta const& lhs, any_iterator_sorta const& rhs ) {
    return !(lhs==rhs);
  }
  any_iterator_sorta(any_iterator_sorta&& o) = default;
  any_iterator_sorta() = default;

  any_iterator_sorta& operator=(any_iterator_sorta const& o) {
    any_iterator_sorta tmp=o;
    std::swap(tmp.pImpl, o.pImpl);
    return *this;
  }
  any_iterator_sorta& operator=(any_iterator_sorta&& o) = default;
private:
  struct pimpl {
    virtual ~pimpl() {}
    virtual void next() = 0;
    virtual T get() const = 0;
    virtual std::unique_ptr< pimpl > clone() const = 0;
    virtual bool equals( pimpl const& rhs ) const = 0;
  };
  std::unique_ptr< pimpl > pImpl;
  template<class It>
  struct pimpl_impl:pimpl {
    It it;
    virtual void next() final override { ++it; }
    virtual T get() const final override { return *it; }
    virtual std::unique_ptr< pimpl > clone() const final override {
      return std::make_unique<pimpl_impl>( it );
    }
    virtual bool equals( pimpl const& rhs ) const final override {
      if (auto* r = dynamic_cast<pimpl_impl const*>(&rhs))
        return it == r->it;
      return false;
    }
    pimpl_impl( It in ):it(std::move(in)) {}
  };
  any_iterator_sorta( std::unique_ptr< pimpl > pin ):pImpl(std::move(pin)) {}
public:
  template<class It,
    std::enable_if_t< !std::is_same<It, any_iterator_sorta>{}, int>* =nullptr
  >
  any_iterator_sorta( It it ):
    pImpl( std::make_unique<pimpl_impl<It>>( std::move(it) ) )
  {}       
};

如果您的接口类返回any_iterator_sorta<T>,其中T是您迭代的类型,并且子类执行相同的操作(但返回了支持++的类,{{1复制构造和*),它会表现为多态的值。

==是一个C ++伪迭代器,它足以在any_iterator_sorta循环中工作,但不能满足标准规定的真正C ++迭代器的所有公理。

live example

测试用例:

for(:)

相同的代码,使用两个不同的迭代器实现进行迭代。

具体而言,假设您的代码迭代void test( any_iterator_sorta<int> begin, any_iterator_sorta<int> end ) { for (auto it = begin; it != end; ++it) { std::cout << *it << '\n'; } } std::vector<int> v{1,2,3}; std::list<int> l{10,11}; test( begin(v), end(v) ); test( begin(l), end(l) ); s:

int
virtual any_iterator_sorta<int> begin() = 0; virtual any_iterator_sorta<int> end() = 0; 中的

。然后在List

ArrayList

最后,实施 any_iterator_sorta<int> begin() final override { return ArrayListIterator{}; } any_iterator_sorta<int> end() final override { return ArrayListIterator{}; }

ArrayListIterator

上面只包含&#34; stub&#34; 4个必需操作的版本(复制构造, class ArrayListIterator{ public: int operator*() const { return 0; } bool operator==( ArrayListIterator const& o ){return true;} void operator++() { /* do nothing for now */ } }; ==和一元++),因此*将出现&#34;空&#34;到C ++ ArrayList循环。