|
Boost : |
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://lists.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
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk