Subject: [boost] [Range] Range adaptor approach for temporary range lifetime issue
From: Michel Morin (mimomorin_at_[hidden])
Date: 2012-06-17 08:25:36


When an rvalue range is adapted in range-based for loop,
the lifetime of the rvalue range ends before the loop body begins.
For example, the following code produces dangling references.

    for (auto x : std::string("Hello world!") | reversed) { /*...*/ }

This is the temporary range lifetime issue.

When I proposed BOOST_FOREACH extension for this issue,
( http://listarchives.boost.org/Archives/boost/2011/04/180591.php )
a solution with "moving an rvalue range into range adaptors"
was suggested in the thread.

I implemented this approach as a range adaptor (with the aid of
rvalue references and decltype C++11 features):

    for (auto x : std::string("Hello world!") | moved | reversed) { /*...*/ }

When an rvalue range is piped to `moved` adaptor, it returns `moved_range`.
`moved_range` stores the following data:

* Moved rvalue range:
The rvalue range is moved and stored in `moved_range`.

* Applied adaptors:
When range adaptors are applied to`moved_range`, they are
not invoked immediately; they are stored in fusion vector.
The stored adaptors are invoked when `begin` or `end`
member functions of `moved_range` are called.
(To prevent repeated applications of the stored range adaptors,
begin and end iterators are cached when they are first computed.)

For example, in `std::string("Hello world!") | moved | reversed`,

1. By applying `moved` adaptor, `moved_range` is created. It stores
`std::string` object moved from `std::string("Hello world!")`. It doesn't
store any adaptors.

2. By applying `reversed` adaptor, another `moved_range` is created.
It stores `std::string` object moved from the previous `moved_range`.
It also stores `reversed` adaptor in fusion vector.

Step 2 is repeated if more adaptors are applied.
Then, in "for-init-statement" of range-based for loop,
the stored adaptors are actually applied (and the result is cached).

The free function version (`boost::adaptors::move`) supports
`std::initializer_list`:

    for (auto x : move( {1, 2, 3, 4, 5} ) | reversed) { /*...*/ }

Note that we cannot support it in the pipe syntax `{1, 2, 3, 4, 5} | moved`
because the C++11 Standard does not allow to use list initializers in
binary operators.

Code attached.
Comments?

Regards,
Michel