
Dne 05.05.2014 19:38, Neil Groves napsal:
I apologise for misunderstanding your original post. In this scenario the range_const_iterator meta-function may validly return a mutable iterator. The purpose of the range_const_iterator meta-function is to define the iterator type for functions that use the const Range& overloads.
Well, the point is not the metafunction, but the `const Range&` overloads. Single-pass iterators usually modify the "generator" (otherwise they could be forward iterators easily). So if they are needed, the "generator" can't be a range itself, but instead needs a wrapper. When it is a wrapper, all is fine and the const and non-const overloads both return the same iterator as is the case of pair of iterators.
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.
This is the key bit.
The potential for lifetime issues when combining temporaries with BOOST_FOREACH is a known problem without a good solution with broad compatibility with C++03. In many cases while the range lifetime may end, the underlying iterators live on. The BOOST_FOREACH library then behaves correctly in many cases where it might look like undefined behaviour ought to occur. There are however some lifetime issues for example applying range adaptors to a container returned by value. The solution, for now at least, is to create a temporary variable before the BOOST_FOREACH statement.
... and fortunately the range constructor accepting non-const reference only enforces that, so a wrapper ends up being fine in this case.
I didn't write BOOST_FOREACH, but I believe that it isn't accepting the temporary rather deliberately to avoid lifetime issues.
It does. And then it binds it const and properly extends it's lifetime, so it also only uses const iterators with it. The problem is wrappers. Because other temporaries in the expression don't get their lifetime extended. And it boils down to following rule: * If the actual object can be iterated using const access (like containers), the object itself has to be adapted to be a range. Wrapping it would cause life-cycle issues with foreach. * If the actual object can *not* be iterated using const access (like std::istream), than the object *must* be wrapped and the fact that the wrapper will take non-const reference will prevent using temporary and avoid life-cycle issues with foreach. * The actual object that can not be iterated using const access (like std::istream) could be made a
What is still a problem is wrapper of a wrapper, but it's enough to simplify that to one level of wrapper.
I believe I understand your aim. I rejected this direction of design since it only helps in a small number of cases. I would love to have a complete solution and have spent considerable time to no avail. In my production code I resort to one of: 1. Use one of the Boost.Range algorithms such as boost::push_back or boost::transform (works with versions of C++ prior to C++11) 2. Add a temporary (works with versions of C++ prior to C++11) 3. Replace BOOST_FOREACH with the C++11 range-base for loop 4. Replace BOOST_FOREACH with boost::for_each
I hope I've understood the issue properly this time around.
Well, fortunately I now do. I was rather confused at the beginning.
BOOST_FOREACH is perfectly fine, provided the right combination of
const and non-const references are used to avoid getting dangling
reference by accident.
--
Jan Hudec