
Dne 05.05.2014 13:40, Neil Groves napsal:
On Mon, May 5, 2014 at 11:03 AM, Jan Hudec <bulb@ucw.cz> wrote:
Hello All,
The documentation of [Single Pass Range][1] in Boost.Range says that there should be both `iterator` and `const_iterator` associated types and both const and non-const overloads for `begin` and `end`. However if it is really a single-pass range, I don't see a way to implement a `const_iterator` that would work on `const` reference to the range, because the `operator++` of the iterator involves calling a non-const method of the range.
I think that your statements indicate that you have a misunderstanding about implementing iterators. The word "const" does not refer to the iterator type itself but to the reference type. It is not possible without resort to very perverse use of the "mutable" keyword to implement a const_iterator to refer to any type. Of course, const_iterator is part of every standard container so this is not an idiom that is specific to Boost.Range.
All containers are at least forward ranges, not merely single pass ones. Since iterating a forward range does not change it, there is no problem for it to have a `const` `begin()` and `end()` methods (returning const iterators). What I had issue with is single pass range. Consider something that behaves like `std::istream`. The `std::istream_iterator` can be only constructed from non-const reference to `std::istream`, because the `std::istream_iterator::operator++` (and the constructor itself) call the `std::istream::operator>>`, which is non-const. Of course for `std::istream` a wrapper is needed anyway, because the stream can return various types, so the wrapper has to specify which one is desired. But if I have a class that behaves similarly, but returns a specific type, I still can't make it a range, because I still can't give it a `begin` that would accept const reference (I _can_ usually give it `end`, because for this kind of objects the end iterator is dummy, but that won't help).
For example the `std::istream_iterator::operator++` calls `std::istream::operator>>`, which is non-const and so there is no `const_istream_iterator`. The `istream_range` cheats around this a little, because the range is a wrapper around the stream, not the stream itself. But when I have similarly behaving class that I can make itself be a range, do I really have to work around this using a wrapper, or can the requirement be relaxed in practice?
I believe that you don't have a problem! A const_iterator can be implemented with non-const member functions. There are examples within the Boost.Range unit test code that show the use of extending Boost.Range for user defined types. These show the various methods for implementing this. There is also documentation that has an example extending via the const and mutable meta-functions here:
http://www.boost.org/doc/libs/1_55_0/libs/range/doc/html/range/reference/ext...
Well, it can't be implemented using non-const methods of the _range_ _itself_. If I have a stream-like object class int_reader { ... int get_next(); // NOT const }; than I can create iterators for it. But the iterators will look like class int_reader_iterator : boost::iterator_facade<int_reader_iterator, int, boost::single_pass_traversal_tag, int const &> { int_reader *reader; // NOT const ... void advance() { value = reader->get_next(); } // must NOT be const HERE ... int_reader_iterator(int_reader &reader) : reader(&reader) { } // can't be const here }; And therefore I can't provide int_reader_iterator range_begin(int_reader_iterator const &); And therefore I can't make `int_reader` a range. I can make a range adapter for it, that will break the const chain like `boost::istream_range` does. Now why I wanted that was that I wanted to work with temporaries and was concerned that I would get dangling reference, because BOOST_FOREACH will extend life of the passed range by binding it to a reference, but that does not extend to arguments of the expression. Basically I thought of writing something like BOOST_FOREACH(int i, boost::input_range<int>(std::ifstream("file.txt"))) ... Now this won't extend the life of the ifstream, but fortunately it won't compile either, because input_range requires a non-const reference. And if I had my object that would directly be range, but didn't have const begin, well, then BOOST_FOREACH wouldn't accept a temporary either, because temporary only binds to const reference. So the object that needs to be modified can't be a temporary whatever I do and I can wrap it and don't need to bother. What is still a problem is wrapper of a wrapper, but it's enough to simplify that to one level of wrapper. -- Jan Hudec <bulb@ucw.cz>