7

So basically what I want to do is to have a pure virtual method returning an iterator to an arbitrary collection of a concrete type, e.g in pseudo code:

virtual Iterator<T> getIterator() const = 0;

The user of this class actually don't care what implementation the child class uses. It could be a set, vector, list, array etc.

I'm aware of the std::iterator class but I cant find a way to specify it correctly in order to work with a simple vector.

virtual std::iterator<std::random_access_iterator_tag,T> getIterator() const = 0;

myVector.begin() // compilation error in implementation

defining std::iterator with const T as type parameter hasn't worked too. I also tried leaving T and instead defining the pointer and reference types as const T* and const T&.

By taking a look at the std::vector implementation, I found out that std::vector::const_iterator actually derives from _Iterator012 deriving from _Iterator_base.

It really bugs me that there isn't any way to work with arbitrary collections in std. Implementing my classes as templates like in <algorithm> is not an option for me due two reasons:

  • No control over the actual value type
  • I simply don't want to make my classes templates complicating my design a lot and making things less flexible.

The used type parameter T was just for demonstration, actually this is a concrete type.

eddie
  • 1,252
  • 3
  • 15
  • 20
Sebastian Hoffmann
  • 11,127
  • 7
  • 49
  • 77
  • 1
    [This article](http://www.artima.com/cppsource/type_erasure.html) could be useful. – juanchopanza Dec 02 '12 at 15:28
  • 2
    This is *precisely* what templates are for and the standard library uses them. They only make your code more flexible! – Joseph Mansfield Dec 02 '12 at 15:30
  • possible duplicate of [Generic iterator](http://stackoverflow.com/questions/9938/generic-iterator) – Lol4t0 Dec 02 '12 at 15:33
  • 1
    So you want templates, without using templates, and it bothers you that, aside from the huge feature of templates, C++ doesn't support templates. Yeah you may be on to something here. – Lightness Races in Orbit Dec 02 '12 at 15:33
  • 5
    @LightnessRacesinOrbit Please leave this question. I simply dont want to argue with someone being that sarcastic for no reason. And for one: I dont see the point explaining my whole architecture jsut to proof that templates are no option for me. In my concrete case I dont want to use them, live with it. Cant anyone come up with concrete solutions instead of discussing paradigmas? – Sebastian Hoffmann Dec 02 '12 at 15:36
  • 1
    If you don't mind dynamic allocation, you could make a simple type-erasing iterator for your value type. – Kerrek SB Dec 02 '12 at 15:39
  • The article I linked explains a solution using type erasure. – juanchopanza Dec 02 '12 at 15:46
  • What about just using a typedef in your class to the appropriate iterator type. The user of the class can just use the typedef and not have to care about what specific type of container is used. – Vaughn Cato Dec 02 '12 at 15:57
  • `std::iterator` isn't what's needed here. It's just a convenience class that saves having to write a handful of `typedef`s; real iterators either derive from it or write those `typedef`s themselves. – Pete Becker Dec 02 '12 at 16:33
  • Yes, because "live with it" is the best way to encourage me to spend my spare time to build solutions for you for free. Leaving as requested. Good luck. – Lightness Races in Orbit Dec 02 '12 at 23:54

2 Answers2

7

Here's a basic and very rudimentary skeleton approach using type erasure. You'll have to fill in a lot of missing details, though!

#include <memory>

template <typename T>
class TEIterator
{
    struct TEImplBase
    {
        virtual ~TEImplBase() { }
        virtual std::unique_ptr<TEImplBase> clone() const = 0;
        virtual void increment() = 0;
        virtual T & getValue() = 0;
        T * getPointer() { return std::addressof(getValue()); }
    };

    template <typename Iter>
    struct TEImpl
    {
        Iter iter;

        TEImpl(Iter i) : iter(i) { }

        virtual T & getValue()
        { return *iter; }

        virtual std::unique_ptr<TEImplBase> clone() const
        { return std::unique_ptr<TEImplBase>(new TEImpl<Iter>(*this)); }

        virtual void increment()
        { ++iter; }
    };

    std::unique_ptr<TEImplBase> impl;

public:

    template <typename T>
    TEClass(T && x)
    : impl(new TEImpl<typename std::decay<T>::type>(std::forward<T>(x)))
    {
    }

    TEClass(TEClass && rhs) = default;

    TEClass(TEClass const & rhs) : impl(rhs.impl.clone()) { }

    TEIterator & operator++()
    {
        impl->increment();
        return *this;
    }

    T & operator*() { return impl->getValue(); }
    T * operator->() { return impl->getPointer(); }
};

Usage:

std::vector<int> v;
std::deque<int> dq;

TEIterator<int> a = v.begin(), b = dq.end();
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • As a note: Some people advocate type erasure much more generously on the grounds that it provides strict value semantics to a polymorphic concept. As you can see, you need boilerplate code that extracts the relevant behaviour from the payload class. There's a proposed Boost library to make those wrapper classes in a systematic fashion, I think. – Kerrek SB Dec 02 '12 at 16:36
  • I would recommend using the boost iterator helpers. Stick a type erased pImpl inside and implement the required methods. – Yakk - Adam Nevraumont Dec 02 '12 at 17:54
0

If you want to use a virtual method, you cannot use an arbitrary return value. What you can do, is define a base class, which is a wrapper around iterators, and subclass from that wrapper class.

But even then, you must restrict yourself to the smallest common denominator, since there are several iterator classes in the C++ standard library.

So, AFAICS, such a method with arbitrary iterators isn't really feasible without using templates.

Olaf Dietsche
  • 72,253
  • 8
  • 102
  • 198
  • 1
    A simple polymorphic class is not so good, though, because iterators need to be passable by value. – Kerrek SB Dec 02 '12 at 15:48
  • @KerrekSB Yes, but ... if you have a base class with a virtual method, and this is what the OP has, there is no way to return arbitrary types. No if or when or type erasure. – Olaf Dietsche Dec 02 '12 at 16:04