I am recently trying to know how ranged-for work,
and finally got a picture as below:
for(obj& temp_ref:range r)//hide iterator somewhere as the range defined.
For each iteration,there is just a update of current object to doing the job in one circle of loop,and this is decent explain for me.
But when I trying to combine range and iterator into a mixed class,
there`s a problem of which operator++ should it call in my code,
in the sense, it should be the derivative class method be call in range-based-for,
because I create the object as derivative object,my declare is virtual.
the code is provided here,the first part is sprated and fine,
but the second one is works weird for me.
#include <iostream>
namespace first
{
template<class iterator>
class range
{
public:
range(iterator ina,iterator inb):a(ina),b(inb){};
iterator a,b;
iterator& begin(){return a;};
iterator& end(){return b;};
};
template<class T>
class iterator
{
public:
iterator(T* ini):i(ini){};
T* i;
virtual iterator<T>& operator= (const iterator<T>& other){this->i=other.i;return *this;};
virtual T& operator* (){return *i;};
virtual T operator!= (const iterator<T>& other){return this->i==other.i?0:1;};
virtual void operator++ (){i++;};
};
class jump2:public iterator<int>
{
public:
jump2(int* ini):iterator<int>(ini){};
virtual void operator++ (){i+=2;};
};
}
namespace second
{
template<class T>
class iterator
{
public:
iterator(T* inStart,T* inFinal):current(inStart),final(inFinal){};
T* current;
T* final;
iterator<T> begin(){return iterator<T>(this->current,this->final);};
iterator<T> end(){return iterator<T>(this->final,this->final);};
virtual iterator<T>& operator= (const iterator<T>& other){this->current=other.current;this->final=other.final;return *this;};
virtual T& operator* (){return *this->current;};
virtual bool operator!= (iterator<T>& other){return this->current!=other.final;};
virtual void operator++ ()
{
std::cout<<"<call base>";
this->current=this->current+1;
};
};
template<class T>
class jumper:public iterator<T>
{
public:
jumper(T* inStart,T* inFinal):iterator<T>(inStart,inFinal){};
void operator++ ()
{
std::cout<<"<call deri>";
this->current=this->current+2;
};
};
};
int main()
{
int a[6]={1,0,2,0,3,0};
//success
{
using namespace first;
range<jump2> a_range(jump2(a),jump2(a+6));
for(int store:a_range)
std::cout<<store<<std::endl;
}//pause();
//Fail
{
using namespace second;
jumper<int> a_iterator_call_jumper(a,a+6);
for(int& i:a_iterator_call_jumper)
std::cout<<i<<std::endl;
}//pause();
return 0;
};
this output is
1
2
3
1
<call base>0
<call base>2
<call base>0
<call base>3
<call base>0
<call base>
but it should be like
1
2
3
1
<call deri>2
<call deri>3
<call deri>
Is this thing goes wrong because I use it wrong?
Or there`s a bug I am not find yet?
template<class iterator>
class range
{
public:
range(iterator ina,iterator inb):a(ina),b(inb){};
iterator a,b;
iterator& begin(){return a;};
iterator& end(){return b;};
};
no
template<class iterator>
class range
{
public:
range(iterator ina,iterator inb):a(ina),b(inb){};
iterator a,b;
iterator begin() const {return a;};
iterator end() const {return b;};
};
yes.
template<class T>
class iterator
{
public:
iterator(T* ini):i(ini){};
T* i;
virtual iterator<T>& operator= (const iterator<T>& other){this->i=other.i;return *this;};
virtual T& operator* (){return *i;};
virtual T operator!= (const iterator<T>& other){return this->i==other.i?0:1;};
virtual void operator++ (){i++;};
};
no
template<class T>
class iterator
{
public:
iterator(T* ini):i(ini){};
T* i;
iterator( iterator const& ) = default;
iterator& operator= (const iterator<T>& other) & = default;
T& operator*() {return *i;};
bool operator!= (const iterator& other) const {return this->i==other.i?0:1;};
void operator++() {++i;};
};
yes.
Don't use the standard C++ vtable-based polymorphic object model for iterators; they should be regular types, and the vtable-based polymorphic object model is not regular.
As for second, it is not a good plan. An iterator and the range over which you iterate are not the same thing. Smashing them together makes your class incoherent.
C++ iteration is not designed for dynamic dispatch (polymorphism). You can do it, but it kills performance, and it isn't trivial to write. You basically need to hide the virtual dispatch behind a regular type (and you can dispatch using vtables, or you can dispatch using C++'s void pointer based type erasure).
If you are used to other languages, many other languages descended from C++ use mandatory reference semantics with garbage collection for objects of class type. Their virtual inheritance object model works on reference variables, as it does in C++ with reference and pointer types.
C++ is relatively quirky (in this day and age) in that it supports complex objects as value types, and that the language and standard library presumed you are working with value types most of the time. (Note that a pointer is a kind of value type; but its value is the pointer, not the object), and that when you want to use reference/pointer semantics you have to adapt your code.
Related
I have a virtual container "ISequence" which is used as a template for container implementations on arrays or lists. I have implemented an iterator for both Array and List containers. What I want to do is to create a virtual IIterator class so I can create algorithms that use iterators and accept ISequence is a parameter. My goal is to be free of implementation.
I tried adding virtual IIterator class to ISequence with no methods and that inherits std::iterator but that didn't work
ISequence class:
template <typename T>
class ISequence {
protected:
int length; //length of sequence
public:
virtual int getLength() const = 0; //get length of sequence
virtual bool getIsEmpty() const = 0; //check if empty
public:
virtual T get(int index) const = 0; //get item based on index
virtual T getFirst() const = 0; //get first item
virtual T getLast() const = 0; //get last item
virtual ISequence<T>* getSubSequence(int startIndex, int endIndex) const = 0;
virtual void append(T item) = 0; //add item to the end
virtual void prepend(T item) = 0; //add item to the beginning
virtual void insertAt(int index, T item) = 0; //insert item at a specific point
virtual void remove(T item) = 0; //remove specific item
virtual void replace(int index, T item) = 0; //replace an item
};
Array with an iterator:
template <typename T>
class ArraySequence: public ISequence<T> {
private:
T* data;
public:
ArraySequence();
ArraySequence(ISequence<T>* sequence);
ArraySequence(int n, int leftLimit, int rightLimit);
ArraySequence<T>& operator=(const ArraySequence<T>& sequence);
~ArraySequence();
public:
virtual int getLength() const override;
virtual bool getIsEmpty() const override;
public:
virtual T get(int index) const override;
virtual T getFirst() const override;
virtual T getLast() const override;
virtual ArraySequence<T>* getSubSequence(int startIndex, int endIndex) const override;
virtual void append(T item) override;
virtual void prepend(T item) override;
virtual void insertAt(int index, T item) override;
virtual void remove(T item) override;
virtual void replace(int index, T item) override;
private:
class MyIterator: public std::iterator<std::random_access_iterator_tag, T> {
friend class ArraySequence;
private:
T* pos;
MyIterator(T* pos);
public:
MyIterator(const MyIterator &it);
~MyIterator();
public:
typename MyIterator::reference operator*() const;
typename MyIterator::pointer operator->() const;
typename MyIterator::reference operator[](const typename MyIterator::difference_type& n) const;
typename MyIterator::difference_type operator-(const MyIterator& it) const;
MyIterator operator++(int);
MyIterator& operator++();
MyIterator operator--(int);
MyIterator& operator--();
MyIterator operator+(const typename MyIterator::difference_type& n) const;
MyIterator& operator+=(const typename MyIterator::difference_type& n);
MyIterator operator-(const typename MyIterator::difference_type& n) const;
MyIterator& operator-=(const typename MyIterator::difference_type& n);
bool operator!=(const MyIterator& it) const;
bool operator==(const MyIterator& it) const;
bool operator<(const MyIterator& it) const;
bool operator>(const MyIterator& it) const;
bool operator<=(const MyIterator& it) const;
bool operator>=(const MyIterator& it) const;
};
public:
typedef MyIterator iterator;
typedef MyIterator const const_iterator;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
};
My code for abstract IIterator (as an addition to ISequence):
protected:
class IIterator: public std::iterator<std::random_access_iterator_tag, T> { //virtual Iterator class
};
public:
typedef IIterator iterator;
typedef IIterator const const_iterator;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
Then I inherit it by MyIterator in Array.
With what I tried I get the "Invalid operands to binary expression ('ISequence::IIterator' and 'ISequence::IIterator')" error message when I run simple forrange cycle.
The concepts you're implementing are rather foundational in C#. Perhaps there's a good reason none of that exists in C++? Yes!
You are trying to implement .Net library using C++. This is not such a great idea, since the design of that library caters to the limitations of C# and the CLR (or whatever they call it these days). C# can do much less with types at compile time, and it doesn't have a compile-time generic type substitution system like C++ does. By not using what C++ offers, you're reinventing the wheel and writing code that looks quite unidiomatic. And it'll likely be slower than it needs to be, since virtual method calls on iterators are liable to be invoked often, their overheads showing. The compiler will devirtualize some of the calls if it can, but that's not something to depend on.
Generalizing a bit; The direct use of most any idiom from the .Net ecosystem in C++ is wrong by default unless proven otherwise.
In C++ the notion of "interfaces" doesn't depend on virtual methods, but on concepts - i.e. constraints on types. There's no way to achieve this in C#, nor in CLR in general, the design doesn't support it. In C++, as long as the concrete type used fits within the constraints that the caller requires (e.g. is iterable), the compiler will take care of the rest. Idiomatically, you'd pass containers around either using ranges or iterators, and all code that accepts those should be generic: the type of the container or iterator should be a template parameter, and the iterators should not be forced to be of the same type - this was really a library design bug in older versions of C++ library.
So, the beauty of C++ is that you don't have to define any interfaces whatsoever. In C++20 you can constrain the types using concepts, but other than that, it's just simple - much simpler than in C#. The idea of ranges is to have objects that act "like" containers, i.e. you can iterate on them using range-for, but they aren't necessarily containers - they may be just a way to express a range of elements on a container, or even dynamically generated.
If you're thinking of doing this in support of porting code from C# to C++, then simply forget about the need for ISequence etc. Just use generic type parameters, optionally constrain them using concepts if you're using a C++20 compiler, and you're set. It's that easy. The container can be anything. Even the plain "C" array (shudder - don't use those, use std::array instead, always!).
Suppose we had following C# code:
System.IO.TextWriter cout = Console.Out;
Action<System.Collections.IEnumerable> printValues = (values) =>
{
cout.WriteLine("printValues");
foreach (var v in values)
cout.Write($"{v} ");
cout.Write("\n");
};
var list_of_ints = new List<int>{1, 2, 3, 4, 5};
var vector_of_strings = new String[]{"a", "b", "c", "d"};
cout.WriteLine("* Entire containers");
printValues(list_of_ints);
printValues(vector_of_strings);
cout.WriteLine("\n* Subranges of containers");
printValues(list_of_ints.GetRange(1, list_of_ints.Count() - 1));
printValues(vector_of_strings.Take(vector_of_strings.Count() - 1));
Output:
* Entire containers
printValues
1 2 3 4 5
printValues
a b c d
* Subranges of containers
printValues
2 3 4 5
printValues
a b c
It wouldn't look much different in C++, as long as you stick to idiomatic C++ and don't try reimplementing .Net in C++:
#include <forward_list>
#include <iostream>
#include <string>
#include <vector>
// This will accept entire containers, as well as C++20 ranges.
template <class C> void printValues(const C &values) {
std::cout << __FUNCTION__ << " with container\n";
for (auto &v : values)
std::cout << v << " ";
std::cout << "\n";
}
// This is the more legacy way of doing it - to stay compatible with C++98 (shiver).
template <class I1, class I2> void printValues(I1 start, I2 end) {
std::cout << __FUNCTION__ << " with iterators\n";
for (; start != end; ++start)
std::cout << *start << " ";
std::cout << "\n";
}
int main() {
std::forward_list<int> list_of_ints{1,2,3,4,5};
std::vector<std::string> vector_of_strings{"a", "b", "c", "d"};
std::cout << "* Entire containers\n";
printValues(list_of_ints);
printValues(vector_of_strings);
std::cout << "\n* Subranges of containers\n";
printValues(std::next(list_of_ints.cbegin()), list_of_ints.cend());
printValues(vector_of_strings.cbegin(), std::prev(vector_of_strings.cend()));
}
Output:
* Entire containers
printValues with container
1 2 3 4 5
printValues with container
a b c d
* Subranges of containers
printValues with iterators
2 3 4 5
printValues with iterators
a b c
I'm actually new to c++. I mostly worked with Java.
I tried to build my own iterator, and after a bit of reading, came up with this method.
template<class T> class Iterable
{
T start,stop;
public:
explicit Iterable(T s,T e) {start=s; stop=e;}
public:
virtual void next(T& i);
public:
class iterator: public std::iterator<
std::input_iterator_tag, // iterator_category
T, // value_type
long, // difference_type
const T*, // pointer
T // reference
>{
T current;
Iterable<T>& obj;
public:
explicit iterator(T t,Iterable<T>& o) : obj(o) {current=t;}
iterator& operator++() {obj.next(current); return *this;}
iterator operator++(int) {iterator retval = *this; ++(*this); return retval;}
bool operator==(iterator other) const {return current == other.current;}
bool operator!=(iterator other) const {return !(*this == other);}
T operator*() const {return current;}
};
iterator begin() {return iterator(start,*this);}
iterator end() {return iterator(stop,*this);}
};
class Range : public Iterable<long>
{
long START,STOP;
public:
void next(long& cur) override
{
if(START>=STOP)
cur++;
else
cur--;
}
public:
Range(long st,long en) : Iterable(st,en) {START=st; STOP=en;}
};
This is my "flex.h" header file. The header compiles OK.
However, when I try to use this Range class, I get the error:
undefined reference to `flex::Iterable<long>::next(long&)'
collect2: error: ld returned 1 exit status
in my compiler (I use g++)
My Test1.cpp file is as follows:
(After includes)
int main()
{
Range range=Range(15,10);
for(auto r : range)
cout << r << "\n";
}
Could someone explain where I went wrong, and how this can be fixed?
Thanks.
You declare a virtual function in next, and don't define it.
A virtual member function has to be defined, be pure virtual, or both. So if you declare
virtual void next(T& i);
It must be defined either inline or outside the class definition because it is not pure virtual. If you intended to make Iterable a template for abstract classes, then adding the pure virtual specifier to next will absolve the error.
virtual void next(T& i) = 0;
I want to know how to have a c++ class to be iterable (stl compatible) without exposing the implementation ?
The structure of the project is like :
Stream
class Stream
{
public:
Stream();
[...]
StreamIterator iter()
{
return StreamIterator(this);
}
private:
class impl;
std::unique_ptr<impl> pimpl;
};
StreamFilter
class StreamFilter
{
public:
StreamFilter();
[...]
private:
class impl;
std::shared_ptr<impl> pimpl;
};
StreamIterator
class StreamIterator
{
public:
StreamIterator(Stream* streamToFilter);
[...]
void addFilter(StreamFilter* filter);
void removeFilter(StreamFilter* filter);
[...]
private:
class impl;
std::unique_ptr<impl> pimpl;
};
StreamFilter is a base class for differents filtering strategies.
For simplicity in the sample code, I used raw memory pointers, of course in a real exploitation code I use intelligents pointers : shared, weak ...
I want to allow the StreamIterator become iterable in a STL way, doing :
StreamIterator iter = stream.iter();
iter.addFilter(new FilterByOffset([...with parameters...]));
for (auto item : iter)
{
[...doing something with filtered items...]
}
The basic way is to add some accessors to allow range-based for loop.
StreamIterator
class StreamIterator
{
public:
StreamIterator(Stream* streamToFilter);
[...]
iterator begin();
iterator end();
const_iterator cbegin() const;
const_iterator cend() const;
[...]
void addFilter(StreamFilter* filter);
void removeFilter(StreamFilter* filter);
[...]
private:
class impl;
std::unique_ptr<impl> pimpl;
};
Where iterator and const_iterator are basically typedef's to the internal container iterators. And this is the problem.
First, I don't want to expose private implementation in the StreamIterator header. And so, iterator and const_iterator are not allowed here.
Second, because of the stream filtering strategy, the iterators returned are not just alias to some internal stl containers. In the internal implementation, I need to call the filters in a functor way to check if the item need to be exclude or not.
The only type allowed in the StreamIterator header is the type of the item object returned.
Is there a way to do that?
Thank you very much!
Additional Information:
Maybe this declaration is a way to allow a private implementation, I need to investigate more :
StreamIterator
class StreamIterator
{
public:
StreamIterator(Stream* streamToFilter);
[...]
struct iterator
{
Object::Ptr operator*();
iterator& operator++();
bool operator!= (const iterator& it) const;
};
typedef typename StreamIterator::iterator iterator;
iterator begin();
iterator end();
[...]
void addFilter(StreamFilter* filter);
void removeFilter(StreamFilter* filter);
[...]
private:
class impl;
std::unique_ptr<impl> pimpl;
};
First, don't call it StreamIterator; an iterator is a pointer-like object for which 2 of them can specify a range. Your StreamIterator doesn't have this. Nothing good can come of reusing the well defined iterator term here.
Now, your StreamIterator is some kind of range of iterators. So we'll call it a StreamRange.
In order to hide how StreamRange can be iterated over, you have to hide how the iterators it uses work.
And this -- hiding the implementation detalis of a stream iterator -- has a substantial cost to it.
When iterating over a loop, each step in the loop involves ++ and * and an ==. Throwing a pointer indirection and a vtable lookup (or equivalent) on each of those will make your loops much slower.
But here is how to do it.
template<class Value>
struct any_input_iterator {
using difference_type = std::ptrdiff_t;
using value_type=Value;
using pointer = value_type*;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
private:
struct vtable_t {
bool(*equal)( std::any const& lhs, std::any const& rhs ) = 0;
Value(*get)( std::any& ) = 0;
void(*inc)( std::any& ) = 0;
};
vtable_t const* vtable = 0;
std::any state;
template<class U>
static vtable_t make_vtable() {
return {
[](std::any const& lhs, std::any const& rhs)->bool {
return std::any_cast<U const&>(lhs) == std::any_cast<U const&>(rhs);
},
[](std::any& src)->Value {
return *std::any_cast<U&>(src);
},
[](std::any& src) {
++std::any_cast<U&>(src);
}
};
}
template<class U>
static vtable_t const* get_vtable() {
static const auto vtable = make_vtable<U>();
return &vtable;
}
public:
template<class U,
std::enable_if_t<!std::is_same<std::decay_t<U>, any_input_iterator>{}, bool> = true
>
any_input_iterator(U&& u):
vtable(get_vtable<std::decay_t<U>>()),
state(std::forward<U>(u))
{}
any_input_iterator& operator++() { vtable->inc(state); return *this; }
any_input_iterator operator++(int) { auto tmp = *this; ++*this; return tmp; }
reference operator*() { return vtable->get(state); }
friend bool operator==( any_input_iterator const& lhs, any_input_iterator const& rhs ) {
if (lhs.vtable != rhs.vtable) return false;
if (!lhs.vtable) return true;
return lhs.vtable->equal( lhs.state, rhs.state );
}
friend bool operator!=( any_input_iterator const& lhs, any_input_iterator const& rhs ) {
return !(lhs==rhs);
}
struct fake_ptr {
Value t;
Value* operator->()&&{ return std::addressof(t); }
};
fake_ptr operator->()const { return {**this}; }
};
there are probably some typoes, but this is basic type erasure. Boost does a better job at this.
I only supported input iterators. If you want to support forward iterators, you have to change up some typedefs and return references and the like.
any_input_iterator<int> begin();
any_input_iterator<int> end();
that is, however, enough to let someone iterate over your range in question.
It will be slow, but it will work.
My requirements are same to the question asked Using Iterators to hide internal container and achieve generic operation over a base container[1] at stackoverflow. I have a generic pure virtual base container class, which needs to provide an iterator which should be STL complaint so I can use them with cpp algorithm's #include <algorithm>. My implementation uses only an single class instead of two classes as in [1] solution.
Base pure virtual class
class BaseItr
{
public:
class iterator : public std::iterator<std::input_iterator_tag, int>
{
public:
iterator() : _in(NULL) {}
inline iterator(const iterator& org) : _in(org._in) {}
inline iterator& operator=(const iterator& other) { _in = other._in; return *this; }
virtual inline int operator * () { return _in->operator*(); }
virtual inline iterator& operator++() { (*_in)++; return *this; }
virtual inline iterator& operator++(int unused) { (*_in)++; return *this; }
virtual inline bool operator==(const iterator& other)
{
return *(*_in) == *(*(other._in));
}
virtual inline bool operator!=(const iterator& other)
{
return *(*_in) != *(*(other._in));
}
// would use shared pointer insted of this
//~iterator() { if(_in) { delete _in; } }
static inline iterator New(iterator *in) { return iterator(in); }
private:
iterator(iterator *in) : _in(in) {}
iterator *_in;
};
virtual iterator begin() = 0;
virtual iterator end() = 0;
};
Implementation
class Itr : public BaseItr
{
private:
class iterator : public BaseItr::iterator
{
public:
iterator(int val) : _val(val), BaseItr::iterator() {}
int operator * () { return _val; }
inline iterator& operator++() { ++_val; return *this; }
inline iterator& operator++(int unused) { _val++; return *this; }
private:
int _val;
};
BaseItr::iterator _begin;
BaseItr::iterator _end;
public:
inline Itr(int start, int end)
{
_begin = BaseItr::iterator::New(new iterator(start));
_end = BaseItr::iterator::New(new iterator(end));
}
BaseItr::iterator begin() { return _begin; }
BaseItr::iterator end() { return _end; }
};
My implementation works was need, I want to know are there any drawbacks with this implementation, Please help me decide with my design to use the appropriate implementation. I have add my full working example code in github:gist https://gist.github.com/3847688
Ref:
Iterator for custom container with derived classes
Using Iterators to hide internal container and achieve generic operation over a base container
Fast and flexible iterator for abstract class
C++ : Using different iterator types in subclasses without breaking the inheritance mechanism
The most glaring issue: your iterator does not have value semantics.
The STL algorithms are free to copy an iterator if they wish. For example suppose:
template <typename It>
It find(It b, It e, typename std::iterator_traits<It>::const_reference t) {
for (; b != e; ++b) {
if (*b == t) { return b; }
}
return e;
}
The problem is that if you invoke this algorithm with BaseItr&, then the result is of type BaseItr, and you are thus exposed to Object Slicing, which is undefined behavior.
In order to give value semantics to you iterator, you need to create a wrapper class around an abstract implementation and have the wrapper correctly manage the copy through a virtual clone method. If your iterator ends up with virtual methods, you are doing it wrong.
Have a quick question about what would be the best way to implement iterators in the following:
Say I have a templated base class 'List' and two subclasses "ListImpl1" and "ListImpl2". The basic requirement of the base class is to be iterable i.e. I can do:
for(List<T>::iterator it = list->begin(); it != list->end(); it++){
...
}
I also want to allow iterator addition e.g.:
for(List<T>::iterator it = list->begin()+5; it != list->end(); it++){
...
}
So the problem is that the implementation of the iterator for ListImpl1 will be different to that for ListImpl2. I got around this by using a wrapper ListIterator containing a pointer to a ListIteratorImpl with subclasses ListIteratorImpl2 and ListIteratorImpl2, but it's all getting pretty messy, especially when you need to implement operator+ in the ListIterator.
Any thoughts on a better design to get around these issues?
If you can get away with making List<T>::iterator non-virtual, then delegating the virtualness off add to List makes things simple:
template<typename T>
class List
{
virtual void add_assign(iterator& left, int right) = 0;
public:
class iterator
{
const List* list;
const T* item;
public:
iterator(const List* list, const T* item) : list(list), item(item) {}
iterator& operator +=(int right)
{
list->add_assign(*this, right);
return *this;
}
static iterator operator +(iterator const& left, int right)
{
iterator result = left;
result += right;
return result;
}
};
virtual iterator begin() const = 0;
virtual iterator end() const = 0;
};
Otherwise (if the iterators need to store significantly different data, for example), then you have to do the regular, boring pointer-to-implementation to get your virtualness:
template<typename T>
class List
{
class ItImpl
{
virtual ItImpl* clone() = 0;
virtual void increment() = 0;
virtual void add(int right) = 0;
};
public:
class iterator
{
ItImpl* impl;
public:
// Boring memory management stuff.
iterator() : impl() {}
iterator(ItImpl* impl) : impl(impl) {}
iterator(iterator const& right) : impl(right.impl->clone()) {}
~iterator() { delete impl; }
iterator& operator=(iterator const& right)
{
delete impl;
impl = right.impl->clone();
return *this;
}
// forward operators to virtual calls through impl.
iterator& operator+=(int right)
{
impl->add(right);
return *this;
}
iterator& operator++()
{
impl->increment();
return *this;
}
};
};
template<typename T>
static List<T>::iterator operator+(List<T>::iterator const& left, int right)
{
List<T>::iterator result = left;
result += right;
return result;
}
template<typename T>
class MagicList : public List<T>
{
class MagicItImpl : public ItImpl
{
const MagicList* list;
const magic* the_magic;
// implement ...
};
public:
iterator begin() const { return iterator(new MagicItImpl(this, begin_magic)); }
iterator end() const { return iterator(new MagicItImpl(this, end_magic)); }
};
There is something very important among iterators, called Iterator Category:
InputIterator
OutputIterator
ForwardIterator
BidirectionalIterator
RandomAccessIterator
Each category define an exact set of operations that are supported, efficiently, by the iterator.
Here, it seems you wish to turn down that powerful identification mechanism to create some kind of bastard category in which the operations are all present, but no guarantee is made on their efficiency.
I think your design smells.
So the problem is that the
implementation of the iterator for
ListImpl1 will be different to that
for ListImpl2. I got around this by
using a wrapper ListIterator
containing a pointer to a
ListIteratorImpl with subclasses
ListIteratorImpl2 and
ListIteratorImpl2, but it's all
getting pretty messy, especially when
you need to implement operator+ in the
ListIterator.
This design is fine IMHO, I can't see anything messy about it. Except for equality and subtraction, operations of the iterator can be implemented by virtual function pretty easily, so you'll have something like
class ListIteratorInterface // abstract
{
protected:
virtual Data& operator*()=0;
// and other operations
};
class ListIteratorA;
class ListIteratorB; // implementation of the above
class ListIterator
{
ListIteratorInterface* impl_;
public:
// when you create this, allocate impl_ on the heap
// operations, forward anything to impl_
};
You could store operator+ as a private virtual method in the base class, and have the iterator call that.
Alternatively, you could consider statically polymorphic list classes, rather than runtime polymorphism.
Say I have a templated base class 'List' and two subclasses "ListImpl1" and "ListImpl2"
What exactly do you gain by using inheritance here?