Boost logo

Boost :

Subject: Re: [boost] Constant iterator of Single Pass Range
From: Jan Hudec (bulb_at_[hidden])
Date: 2014-05-06 15:48:57

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&`
Single-pass iterators usually modify the "generator" (otherwise they
be forward iterators easily). So if they are needed, the "generator"
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
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,
it also only uses const iterators with it.

The problem is wrappers. Because other temporaries in the expression
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 <bulb_at_[hidden]>

Boost list run by bdawes at, gregod at, cpdaniel at, john at