Index: boost/range/adaptor/extend_temporary.hpp =================================================================== --- boost/range/adaptor/extend_temporary.hpp (revision 0) +++ boost/range/adaptor/extend_temporary.hpp (working copy) @@ -0,0 +1,54 @@ +// Boost.Range library +// +// Copyright Google 2013. Use, modification and +// distribution is subject to the Boost Software License, Version +// 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// For more information, see http://www.boost.org/libs/range/ +// + +#ifndef BOOST_RANGE_ADAPTOR_EXTEND_TEMPORARY_HPP +#define BOOST_RANGE_ADAPTOR_EXTEND_TEMPORARY_HPP + +#include + +namespace boost +{ + // Pass a template argument that has been deduced as an rvalue + // reference, and std::forward the function argument. + // extend_temporary will move temporaries into a member + // variable, but not store anything for non-temporaries. + // + // Call this->extended(function_arg) to get a reference to either + // the lifetime-extended temporary or the lvalue argument. + template< class T > + struct extend_temporary + { + private: + T extended_; + protected: +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // No need to provide an alternative for C++98: they'll all + // use the specialization below. + extend_temporary(T&& to_extend) : extended_(std::forward(to_extend)) + { } +#endif + T& extended(T&) + { return extended_; } + }; + + template< class T > + struct extend_temporary< T& > + { + // Empty. + protected: + extend_temporary(T&) + { } + + T& extended(T& lval) + { return lval; } + }; +} + +#endif // BOOST_RANGE_ADAPTOR_EXTEND_TEMPORARY_HPP Index: boost/range/adaptor/filtered.hpp =================================================================== --- boost/range/adaptor/filtered.hpp (revision 84524) +++ boost/range/adaptor/filtered.hpp (working copy) @@ -1,6 +1,6 @@ // Boost.Range library // -// Copyright Thorsten Ottosen, Neil Groves 2006 - 2008. Use, modification and +// Copyright Thorsten Ottosen, Neil Groves 2006 - 2008, Google 2013. Use, modification and // distribution is subject to the Boost Software License, Version // 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) @@ -12,6 +12,7 @@ #define BOOST_RANGE_ADAPTOR_FILTERED_HPP #include +#include #include #include @@ -19,9 +20,12 @@ namespace boost { namespace range_detail { + // Note that R is an lvalue reference type if the filter was + // passed an lvalue range. template< class P, class R > struct filtered_range : - boost::iterator_range< + private boost::extend_temporary, + public boost::iterator_range< boost::filter_iterator< P, BOOST_DEDUCED_TYPENAME range_iterator::type > @@ -34,9 +38,15 @@ namespace boost > > base; public: - filtered_range( P p, R& r ) - : base( make_filter_iterator( p, boost::begin(r), boost::end(r) ), - make_filter_iterator( p, boost::end(r), boost::end(r) ) ) +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + filtered_range( P p, R&& r ) + : extend_temporary(std::forward(r)), +#else // BOOST_NO_CXX11_RVALUE_REFERENCES + filtered_range( P p, R r ) + : extend_temporary(r), +#endif // BOOST_NO_CXX11_RVALUE_REFERENCES + base( make_filter_iterator( p, boost::begin(this->extended(r)), boost::end(this->extended(r)) ), + make_filter_iterator( p, boost::end(this->extended(r)), boost::end(this->extended(r)) ) ) { } }; @@ -47,21 +57,32 @@ namespace boost { } }; +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES template< class InputRng, class Predicate > inline filtered_range + operator|( InputRng&& r, + const filter_holder& f ) + { + return filtered_range( + f.val, std::forward(r) ); + } +#else // BOOST_NO_CXX11_RVALUE_REFERENCES + template< class InputRng, class Predicate > + inline filtered_range operator|( InputRng& r, const filter_holder& f ) { - return filtered_range( f.val, r ); + return filtered_range( f.val, r ); } template< class InputRng, class Predicate > - inline filtered_range + inline filtered_range operator|( const InputRng& r, const filter_holder& f ) { - return filtered_range( f.val, r ); + return filtered_range( f.val, r ); } +#endif // BOOST_NO_CXX11_RVALUE_REFERENCES } // 'range_detail' @@ -81,19 +102,28 @@ namespace boost range_detail::forwarder(); } +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES template inline filtered_range - filter(InputRange& rng, Predicate filter_pred) + filter(InputRange&& rng, Predicate filter_pred) { return range_detail::filtered_range( filter_pred, rng ); } +#else // BOOST_NO_CXX11_RVALUE_REFERENCES + template + inline filtered_range + filter(InputRange& rng, Predicate filter_pred) + { + return range_detail::filtered_range( filter_pred, rng ); + } template - inline filtered_range + inline filtered_range filter(const InputRange& rng, Predicate filter_pred) { - return range_detail::filtered_range( filter_pred, rng ); + return range_detail::filtered_range( filter_pred, rng ); } +#endif // BOOST_NO_CXX11_RVALUE_REFERENCES } // 'adaptors' } Index: boost/range/iterator.hpp =================================================================== --- boost/range/iterator.hpp (revision 84524) +++ boost/range/iterator.hpp (working copy) @@ -1,6 +1,6 @@ // Boost.Range library // -// Copyright Thorsten Ottosen 2003-2004. Use, modification and +// Copyright Thorsten Ottosen 2003-2004, Google 2013. Use, modification and // distribution is subject to the Boost Software License, Version // 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) @@ -20,6 +20,7 @@ #include #include #include +#include #include namespace boost @@ -50,17 +51,19 @@ namespace boost template< typename C > struct range_iterator { + typedef BOOST_RANGE_DEDUCED_TYPENAME + remove_reference::type without_reference; #if BOOST_WORKAROUND(BOOST_MSVC, == 1310) typedef BOOST_RANGE_DEDUCED_TYPENAME - range_detail_vc7_1::range_iterator::type type; + range_detail_vc7_1::range_iterator::type type; #else typedef BOOST_RANGE_DEDUCED_TYPENAME - mpl::eval_if_c< is_const::value, - range_const_iterator< typename remove_const::type >, - range_mutable_iterator >::type type; + mpl::eval_if_c< is_const::value, + range_const_iterator< typename remove_const::type >, + range_mutable_iterator >::type type; #endif }; Index: libs/range/doc/reference/adaptors/filtered.qbk =================================================================== --- libs/range/doc/reference/adaptors/filtered.qbk (revision 84524) +++ libs/range/doc/reference/adaptors/filtered.qbk (working copy) @@ -1,5 +1,5 @@ [/ - Copyright 2010 Neil Groves + Copyright 2010 Neil Groves, 2013 Google Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) /] @@ -15,7 +15,6 @@ * [*Postcondition:] For all adjacent elements `[x]` in the returned range, `pred(x)` is `true`. * [*Throws:] Whatever the copy constructor of `pred` might throw. * [*Range Category:] __forward_range__ -* [*Range Return Type:] `boost::filtered_range` * [*Returned Range Category:] The minimum of the range category of `rng` and __bidirectional_range__ [section:filtered_example filtered example] Index: libs/range/doc/reference/adaptors.qbk =================================================================== --- libs/range/doc/reference/adaptors.qbk (revision 84524) +++ libs/range/doc/reference/adaptors.qbk (working copy) @@ -1,5 +1,5 @@ [/ - Copyright 2010 Neil Groves + Copyright 2010 Neil Groves, 2013 Google Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) /] @@ -143,25 +143,27 @@ Also note that `boost::range_value::type` must Range Category in the following adaptor descriptions refers to the minimum range concept required by the range passed to the adaptor. The resultant range is a model of the same range concept as the input range unless specified otherwise. -Returned Range Category is the concept of the returned range. In some cases the returned range is of a lesser category than the range passed to the adaptor. For example, the `filtered` adaptor returns only a `ForwardRange` regardless of the input. +Returned Range Category is the concept of the returned range. In some cases the returned range is of a lesser category than the range passed to the adaptor. For example, the `filtered` adaptor returns at most a `BidirectionalRange` regardless of the input. Furthermore, the following rules apply to any expression of the form `` rng | boost::adaptors::adaptor_generator `` -1. Applying `operator|()` to a range `R` (always left argument) and a range adapter `RA` (always right argument) yields a new range type which may not conform to the same range concept as `R`. +# Applying `operator|()` to a range `R` (always left argument) and a range adapter `RA` (always right argument) yields a new range type which may not conform to the same range concept as `R`. -2. The return-type of `operator|()` is otherwise unspecified. +# The return-type of `operator|()` is otherwise unspecified. -3. `operator|()` is found by Argument Dependent Lookup (ADL) because a range adaptor is implemented in namespace `boost::adaptors`. +# `operator|()` is found by Argument Dependent Lookup (ADL) because a range adaptor is implemented in namespace `boost::adaptors`. -4. `operator|()` is used to add new behaviour ['*lazily*] and never modifies its left argument. +# When compiled in at least C++11 and passed a temporary (rvalue) range as the left argument, `operator|()` will move the range into its return value. -5. All iterators extracted from the left argument are extracted using qualified calls to `boost::begin()` and `boost::end()`. +# `operator|()` is used to add new behaviour ['*lazily*], and never modifies its left argument, except possibly to move it as mentioned above. -6. In addition to the `throw`-clauses below, `operator|()` may throw exceptions as a result of copying iterators. If such copying cannot throw an exception, then neither can the whole expression. +# All iterators extracted from the left argument are extracted using qualified calls to `boost::begin()` and `boost::end()`. +# In addition to the `throw`-clauses below, `operator|()` may throw exceptions as a result of copying iterators or moving rvalue ranges. If such copying or moving cannot throw an exception, then neither can the whole expression. + [endsect] [section:reference Reference] Index: libs/range/doc/reference/extending.qbk =================================================================== --- libs/range/doc/reference/extending.qbk (revision 84524) +++ libs/range/doc/reference/extending.qbk (working copy) @@ -1,5 +1,5 @@ [/ - Copyright 2010 Neil Groves + Copyright 2010 Neil Groves, 2013 Google Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) /] @@ -171,11 +171,13 @@ To implement a Range Adaptor without arguments (e. # Provide a range for your return type, for example: `` #include +#include #include template< typename R > struct reverse_range : - boost::iterator_range< + private boost::extend_temporary, + public boost::iterator_range< boost::reverse_iterator< typename boost::range_iterator::type> > { @@ -188,8 +190,10 @@ struct reverse_range : typedef boost::reverse_iterator< typename boost::range_iterator::type > iterator; - reverse_range(R& r) - : base(iterator(boost::end(r)), iterator(boost::begin(r))) + reverse_range(R&& r) + : boost::extend_temporary(std::forward(r)), + base(iterator(boost::end(this->extended(r))), + iterator(boost::begin(this->extended(r)))) { } }; `` @@ -205,17 +209,10 @@ namespace detail { `` template< class BidirectionalRng > inline reverse_range -operator|( BidirectionalRng& r, detail::reverse_forwarder ) +operator|( BidirectionalRng&& r, detail::reverse_forwarder ) { - return reverse_range( r ); + return reverse_range( std::forward(r) ); } - -template< class BidirectionalRng > -inline reverse_range -operator|( const BidirectionalRng& r, detail::reverse_forwarder ) -{ - return reverse_range( r ); -} `` # Declare the adaptor itself (it is a variable of the tag type). @@ -259,8 +256,9 @@ class replace_value template class replace_range -: public boost::iterator_range< - boost::transform_iterator< +: private boost::extend_temporary, + public boost::iterator_range< + public boost::transform_iterator< replace_value::type>, typename boost::range_iterator::type> > { @@ -272,9 +270,10 @@ class replace_range typedef boost::iterator_range base_t; public: - replace_range(Range& rng, value_type from, value_type to) - : base_t(replaced_iterator(boost::begin(rng), Fn(from,to)), - replaced_iterator(boost::end(rng), Fn(from,to))) + replace_range(Range&& rng, value_type from, value_type to) + : boost::extend_temporary(std::forward(rng)), + base_t(replaced_iterator(boost::begin(this->extended(rng)), Fn(from,to)), + replaced_iterator(boost::end(this->extended(rng)), Fn(from,to))) { } }; @@ -305,19 +304,11 @@ replaced = boost::range_detail::forwarder2 inline replace_range -operator|(SinglePassRange& rng, +operator|(SinglePassRange&& rng, const replace_holder::type>& f) { - return replace_range(rng, f.val1, f.val2); + return replace_range(std::forward(rng), f.val1, f.val2); } - -template -inline replace_range -operator|(const SinglePassRange& rng, - const replace_holder::type>& f) -{ - return replace_range(rng, f.val1, f.val2); -} `` [endsect] Index: libs/range/doc/reference/utilities.qbk =================================================================== --- libs/range/doc/reference/utilities.qbk (revision 84524) +++ libs/range/doc/reference/utilities.qbk (working copy) @@ -1,5 +1,5 @@ [/ - Copyright 2010 Neil Groves + Copyright 2010 Neil Groves, 2013 Google Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) /] @@ -9,12 +9,17 @@ Having an abstraction that encapsulates a pair of * Class `iterator_range` * Class `sub_range` -* Function `join` The `iterator_range` class is templated on an __forward_traversal_iterator__ and should be used whenever fairly general code is needed. The `sub_range` class is templated on an __forward_range__ and it is less general, but a bit easier to use since its template argument is easier to specify. The biggest difference is, however, that a `sub_range` can propagate constness because it knows what a corresponding `const_iterator` is. Both classes can be used as ranges since they implement the __minimal_interface__ required for this to work automatically. +This library also provides two further utilities: + +* Function `join` +* Class `extend_temporary` + + [section:iterator_range Class `iterator_range`] The intention of the `iterator_range` class is to encapsulate two iterators so they fulfill the __forward_range__ concept. A few other functions are also provided for convenience. @@ -303,5 +308,123 @@ The expression `join(irange(0,5), irange(5,10))` w [endsect] +[section:extend_temporary Class `extend_temporary`] + +C++11 added the ability to easily capture expression templates into local variables: + +`` +auto&& new_range = MakeVector() | filtered(pred) | reversed; +`` + +However, the above statement includes two temporaries (`MakeVector()` and `MakeVector() | filtered(pred)`) that are destroyed at the end of the statement\u2014well before `new_range` is destroyed. The solution is to detect temporaries with rvalue reference parameters and move them into the adapted range. + +The simplest way of doing this stores a `T&` field in the return value of `lvalue | adaptor`. This is fine if the adaptor needs to store a reference regardless, but most of the adaptors in this library already store iterators in their member variables, which makes the extra `T&` field a waste of space. To fix this, we provide the `extend_temporary` class, which is empty in the case of an lvalue left argument. + +[h4 Synopsis] + +`` +namespace boost +{ + template< class LeftArgumentType > + class extend_temporary + { + LeftArgumentType storage; // Exposition only; absent if LeftArgumentType is an lvalue reference. + + public: // construction + extend_temporary( LeftArgumentType&& forwarded_left_argument ); + + public: // access + LeftArgumentType& extended(LeftArgumentType& lvalue_left_argument); + }; +} // namespace 'boost' +`` + +`extend_temporary` is an empty class. + +This class is used as follows: + +`` +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +template< class P, class Range > struct filtered_range; + +template< class Range, class Predicate > +inline filtered_range +operator|( Range&& r, // <- Note deduced rvalue reference. + const filter_holder& f ) +{ + return filtered_range( // <- Instantiate filtered_range with template param. + f.val, std::forward(r) ); // <- Forward argument into constructor. +} + +template< class P, class Range > +struct filtered_range : + private boost::extend_temporary, ... +{ + filtered_range( P p, Range&& r ) + : extend_temporary(std::forward(r)), + impl(this->extended(r)) {} // <- Use a reference either to the lifetime-extended + // argument or the direct argument. +}; +#endif // BOOST_NO_CXX11_RVALUE_REFERENCES +`` + +# Define a function template taking a deduced rvalue reference parameter (`Range`), so that the template parameter becomes an lvalue reference for named arguments but a non-reference for temporary arguments. + +# Define a wrapper struct template`` that inherits from `extend_temporary`. + +# Pass the unmodified template parameter to the wrapper struct. + +# Pass `std::forward(function_arg)` into the wrapper struct and thence into `extend_temporary`. + +# In the constructor of the wrapper struct, anywhere you need to use a reference to the argument's value, use `this->extended(function_arg)` instead, to get a reference to the internal storage when necessary. + + +In C++98 mode, you can either drop the use of `extend_temporary` entirely, or you can instantiate it with lvalue references to turn it off: + +`` +#ifdef BOOST_NO_CXX11_RVALUE_REFERENCES +template< class P, class Range > struct filtered_range; + +// Both const and non-const lvalue reference overloads: +template< class Range, class Predicate > +inline filtered_range // Range&, not just Range. +operator|( Range& r, + const filter_holder& f ) +{ + // And pass exactly the argument type and value into filtered_range: + return filtered_range( f.val, r ); +} + +template< class Range, class Predicate > +inline filtered_range // const Range&, not just Range. +operator|( const Range& r, + const filter_holder& f ) +{ + return filtered_range( f.val, r ); +} + +template< class P, class Range > +struct filtered_range : + private boost::extend_temporary, ... +{ + filtered_range( P p, Range r ) // <- Remember that Range is a reference type in this case. + : extend_temporary(r), + impl(this->extended(r)) {} // <- Returns 'r' since 'Range' is a reference. +}; +#endif // BOOST_NO_CXX11_RVALUE_REFERENCES +`` + +[h4 Details member functions] + +`extend_temporary( LeftArgumentType&& );` + +[:['[*Effects]] If `LeftArgumentType&&` is an rvalue reference, moves it into `storage`. If `LeftArgumentType&&` is an lvalue reference, has no effect.] + +`LeftArgumentType& extended(LeftArgumentType& orig_arg]` + +[:['[*Returns]] If `LeftArgumentType` is a lvalue reference, `orig_arg`. Otherwise `storage`.] + [endsect] +[endsect] + Index: libs/range/test/adaptor_test/filtered.cpp =================================================================== --- libs/range/test/adaptor_test/filtered.cpp (revision 84524) +++ libs/range/test/adaptor_test/filtered.cpp (working copy) @@ -1,6 +1,6 @@ // Boost.Range library // -// Copyright Neil Groves 2009. Use, modification and +// Copyright Neil Groves 2009, Google 2013. Use, modification and // distribution is subject to the Boost Software License, Version // 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) @@ -83,6 +83,31 @@ namespace boost reference_result.end(), test_result2.begin(), test_result2.end() ); + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // Check that temporary arguments can be captured into + // either an auto-typed variable or a range-based for + // loop. +#ifndef BOOST_NO_CXX11_AUTO_DECLARATIONS + std::vector< value_t > test_result3; + auto filter_result = Container(c) | filtered(pred); + boost::push_back(test_result3, filter_result); + BOOST_CHECK_EQUAL_COLLECTIONS( reference_result.begin(), + reference_result.end(), + test_result3.begin(), + test_result3.end() ); +#endif + +#ifndef BOOST_NO_CXX11_RANGE_BASED_FOR + std::vector< value_t > test_result4; + for (const value_t& elem : Container(c) | filtered(pred)) + test_result4.push_back( elem ); + BOOST_CHECK_EQUAL_COLLECTIONS( reference_result.begin(), + reference_result.end(), + test_result4.begin(), + test_result4.end() ); +#endif +#endif // BOOST_NO_CXX11_RVALUE_REFERENCES } template< class Container, class Pred >