0
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(){

        }
};

I want to implement iterator in this way . But compiler showing Error

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

Same for LinkedList.

As i search on stack overflow for same problem and i get this solution

Iterator for custom container with derived classes and i get two solutions

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

But i want to know that is there any possible way so that iterator will be inner class of ArrayList , LinkedList and List ??

Community
  • 1
  • 1
  • 3
    What is the point of your design? This looks like the design of someone used to C#/Java with its hatred of value types and love of interfaces writing C++ code. – Yakk - Adam Nevraumont Jul 20 '16 at 16:06
  • i want to design an ArrayList class and LinkedList class by using List interface . – VikAsh KuMar Jul 20 '16 at 16:08
  • 4
    So, the point of your design is your design. That isn't a reason to have a design. A reason to do a design is what the design is *used* for. – Yakk - Adam Nevraumont Jul 20 '16 at 16:10
  • My idea for this design is to use iterator in following ways . int main(){ ArrayList ob; List *ptr = &ob; List::iterator it = ptr->begin(); /* Iterate Array List */ LinkedList ob2; List *ptr2 = &ob2; List::iterator it2 = ptr2->begin(); /* Iterate Linked List */ return 0; } – VikAsh KuMar Jul 20 '16 at 16:18
  • 1
    Standard library containers do not use inheritance based polymorphism. Ignore this fact to your own peril. – n. m. could be an AI Jul 20 '16 at 16:19
  • So , @n.m. i have to use Solution 1 as i have mentioned in this post ? – VikAsh KuMar Jul 20 '16 at 16:50
  • 1
    No, you can use polymorphism to do it if you want, but you need to be aware that that's not how the stl works if you want to use your classes with the stl – ROX Jul 20 '16 at 16:54
  • Do not use runtime polymorphism to build an hierarchy of container types. In other words, no abstract List that begets ArrayList and LinkedList. Such a structure just makes no sense. Once you get rid of this, hierarchy of iterators becames a non-issue. – n. m. could be an AI Jul 20 '16 at 17:06
  • Its not really a hierarchy of container types its just giving a couple of types a common base interface. It does make sense and it does work. It's basically what happens with IEnumerator / IEnumerable in C#. And the language difference doesn't change whether the concept makes sense. – ROX Jul 20 '16 at 17:20
  • @ROX I didn't say it cannot work, I said it makes no sense, and this does depend on the language. C# uses a completely different object model from C++. You can make C++ behave almost like C#, but should you? – n. m. could be an AI Jul 21 '16 at 05:38

2 Answers2

-1

you need ArrayListIterator to derive from ListIterator.

The types returned by begin in the derived classes are not derived from the type returned by begin in the base List class.

That is why the error says they are not covariant.

ROX
  • 1,256
  • 8
  • 18
  • After deriving ListIterator to ArrayListIterator gives same Error . – VikAsh KuMar Jul 20 '16 at 16:49
  • as lorro points out, you'd also need begin to return by pointer (or reference) so that it would work polymorphically. – ROX Jul 20 '16 at 16:52
  • -1 why? The error in message in the original post is caused by the reason as explained. Would take a long time to go into whether the whole idea is sensible. – ROX Jul 20 '16 at 17:01
  • I wasn't the -1, but your answer does not solve the OP's problem. The code still fails to compile after the OP makes the change you suggested. Fold the pointer suggestion in and you are getting somewhere, but that is not in your answer. And that means you are telling the OP to manually manage pointer-owned resources, which by itself might be worth a -1! – Yakk - Adam Nevraumont Jul 20 '16 at 18:03
  • Yes, my answer fails in the context of implementing the std collections interface in that begin doesn't return by pointer/reference in a standard collection, I was going to edit to suggest returning an object using non-virtual interface pattern to wrap the container-specific iteration implementation, but you beat me to that. – ROX Jul 20 '16 at 19:03
-1

You need to throw out inheritance based polymorphism. C++ iteration in the standard library and in for(:) loops assumes value-based iterators, and inheritance based polymorphism doesn't work with value-based iterators.

Instead of inheritance-based polymorphism, you should use type-erasure based polymorphism. This requires a bit of boilerplate, because the need abstractly deal with an iterable range was found to be not that common in C++ and often a serious performance hit.

But I'll show you how.

Type erasure based polymorphism is like std::function.

One concrete approach to type-erased polymorphism:

You determine what interface you want to support. Call this your "type erasure target". Your type erasure target is not a class with virtual and =0, but rather a set of functions, methods and operations you want to support, and their signature, and a description of what each does.

Then you write a value class that implements that interface (again, no inheritance) that contains a pImpl (see pImpl pattern) that it dispatches its operations to (the pImpl need not match the same interface, it just needs primitives that you can implement the operations in terms of. Being minimal here is worthwhile).

The pImpl type does have virtual methods and =0 abstract methods.

Then you write a constructor or factory function that takes an object that supports the interface you want to, and generates a concrete instance of the pImpl, then wraps the value class around it.

Suppose the type erasure target was "print to a stream".

My type erasure target is std::ostream& << foo works, and prints stuff.

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) ) )
  {}
};

which may have typos, but you get the idea.

Boost has a generic iterator and range that your base class can return, if you want to find a sample implementation.

There are significant performance hits to using type erasure based iteration: you'll end up with C++ code that is as slow as C#/Java code.

In your case you need to take every thing that iterator requires (copy, increment, move, dereference, etc) as mandated by the standard, and erase it like I did copy. In this case it isn't a view, so your impl impl will likely hold a T not a T*.

Here is a really simple toy for(:) supporing toy pseudo-iterator that will permit your List base to be used in for(:) loops.

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) ) )
  {}       
};

If your interface class returned an any_iterator_sorta<T> where T is the type you iterate over, and the child classes did the same (but returned a class that supports ++, *, copy construct and ==), it would behave polymorphically with values.

The any_iterator_sorta is an C++ pseudo-iterator that is good enough to work in for(:) loops, but does not satisfy all the axioms of a real C++ iterator as mandated by the standard.

live example

Test case:

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) );

same code, iterating using two different iterator implementations.

To be concrete, assuming your code iterates over ints:

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

in List. Then in ArrayList:

   any_iterator_sorta<int> begin() final override {
     return ArrayListIterator{};
   }
   any_iterator_sorta<int> end() final override {
     return ArrayListIterator{};
   }

finally, implement ArrayListIterator:

   class ArrayListIterator{

   public:
     int operator*() const { return 0; }
     bool operator==( ArrayListIterator const& o ){return true;}
     void operator++() { /* do nothing for now */ }
   };

the above just contains "stub" versions of the 4 required operations (copy construct, ==, ++ and unary *), so the ArrayList will appear "empty" to C++ for(:) loops.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • As concept iteration and polymorphism are not incompatible. It's true that the stl does not work that way but to assert that idea need to throw out polymorphism is incorrect. Its often useful to be able to code against a collection that you know will iterator over a certain type without having to know whether the collection type is a vector / list / array whatever. If these iterators are for use in stl algorithms (which OP doesn't state) then that's different. – ROX Jul 20 '16 at 17:06
  • @ROX C++ standard iterators are value types. Value types are not (inheritance based) polymorphic. If the OP is using the term "iterator" to mean something different than that "iterators" mean in C++, I'd be surprised, and advise the OP to change their use of terms. I described how to do polymorphic value types, which to match the C++ standard "iteration" concept you must do. This includes `for(:)` loop support, beyond the `std` algorithms. – Yakk - Adam Nevraumont Jul 20 '16 at 17:30
  • I was thinking iterators as a pattern - the "about" on SOs iterator tag covers that pretty well. Do you have a specific suggestion on how one might "change their use of terms" when talking about iterator pattern but not necessarily specifically one which follows the stl design? – ROX Jul 21 '16 at 11:07