A part of the source code for a project I’m working on, which is responsible for compressing a sequence of ‘events’, looks like this:
#include <iterator>
#include <list>
typedef int Event;
typedef std::list<Event> EventList;
struct Compressor {
// Returns an iterator behind the last element which was 'eaten'
virtual EventList::const_iterator eatEvents( const EventList &l ) = 0;
};
// Plenty of Compressor subclasses exist
void compressAndCopyEatenEvents( Compressor &c ) {
EventList e;
e.push_back( 1 );
EventList::const_iterator newEnd = c.eatEvents( e );
EventList eatenEvents;
std::copy( e.begin(), newEnd, std::back_inserter( eatenEvents ) ); // barfs
}
The issue here is that the compressAndCopyEatenEvents function has a non-const list of events; this list os passed to the eatEvents methods, which takes a reference-to-const and yields a const_iterator. Now the compressAndCopyEatenEvenst function would like to copy the range of eaten events away, so it decides to use some algorithm (std::copy here, which of course could just as well be replaced with the right std::list constructor call – the point is that this problem exists with all kinds of ranges).
Unfortunately(?) many (if not all?) ranges need to be composed from the same iterator type. However, in the above code, ‘e.begin()’ yields an EventList::iterator (because the object is not const) but ‘newEnd’ is an EventList::const_iterator.
Is there a design weakness here which causes this mess? How would you tackle it?
In C++03 the only possible way is to cast. (which is ugly, which is a design flaw, yes).
or have another named variable:
In C++11 you have
cbeginandcendfunctions (that always returnconst_iterators) so you’d do simply