Boost logo

Boost :

Subject: Re: [boost] [range] adaptors vs. rvalue to lvalue& binding
From: Bronek Kozicki (brok_at_[hidden])
Date: 2012-03-22 06:30:06


On 22/03/2012 06:40, Arno Schödl wrote:
> Hello,
>
> ideally I would like to use range-adapted lvalue ranges like any
> other lvalue ranges. Writing it down naively, as below, does not
> compile because LoadRefRange takes (and should take?) the range it
> modifies by lvalue&, while adaptor expressions are rvalues.
>
> template<typename Range>
> void modifies_range( Range& rng );
>
> A a[5];
>
> main() {
> // modifies all elements of a
> modifies_range ( a );
>
> // modifies all elements (except for one) of a
> // adaptor is rvalue, does not compile:
> modifies_range (
> boost::range::join(
> boost::adaptors::slice( a, 0, 1 ),
> boost::adaptors::slice( a, 2, countof(a) )
> )
> );
>
> // compiles, but ugly:
> auto rng=boost::range::join(
> boost::adaptors::slice( a, 0, 1 ),
> boost::adaptors::slice( a, 2, countof(a) )
> );
> modifies_range(rng);
> }
>
>
> Any thoughts? Should adaptors receiving lvalues offer an
>
> operator adaptor&()&&; // overload on rvalue-ness

Hello

you don't want to add such operator to any class, as it would trigger
automatic conversion of rvalue of given type to lvalue. Sometimes this
is not right thing to do, but with added operator you will have no means
of disabling this behaviour. Apart from that, ref-qualifiers
unfortunately are not yet widely implemented and such code won't be very
portable anyway (I only know of 2 compilers implementing this feature).

What you want to do instead is to change the signature of your function
to accept both lvalue and rvalue reference:

   template<typename Range>
   void modifies_range(Range && rng);

I know it seems counter-intuitive since only && is used above, but it
will work with both lvalues and rvalues thanks to reference collapsing
rules. When Range is a reference type, && following it will do nothing
(being collapsed) and lvalue binding will apply; when Range is an rvalue
you will have proper rvalue binding; in both cases parameter "rng" is
always an lvalue when used inside the function, since it's named; also
in both cases rvalue-sness or lvalue-sness of actual function parameter
is preserved in type Range, so it can be "recovered" e.g. with
std::forward<Range>(rng).

You can find more detailed explanation of how this works on Thomas
Becker's explanation of rvalue-references:

http://thbecker.net/articles/rvalue_references/section_08.html

B.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk