Boost logo

Boost :

From: Eric Niebler (eric_at_[hidden])
Date: 2008-04-05 17:41:48


Peter Dimov wrote:
> Eric Niebler:
> ...
>
>> Like a good citizen, I've written make_foo's function call operator to
>> recognize reference_wrapped and non-const-ref arguments as representing
>> lvalues and all others as rvalues.
>
> Const-ref arguments are lvalues. I'm not sure why do you need the
> reference_wrapper support.

Not so. make_foo()(1) -- 1 binds to the const-ref, but it is not an lvalue.

>> The strictly correct thing to do would be to wrap lvalues in
>> reference_wrappers before passing them to F. If I don't, make_foo will
>> do the wrong thing.
>
> I don't see why. Lvalues are lvalues. Wrapping them in a reference_wrapper
> won't make them any more lvaluish. I'm probably missing something.

Indeed.

>> struct blarg
>> {
>> template<typename This, typename Arg>
>> struct result< This( Arg ) >
>> {
>> // OK, if Arg is a reference, then it's an lvalue!
>> };
>>
>> template<typename Arg>
>> typename result< make_foo( ??? ) >::type
>> operator()( Arg const & arg )
>> {
>> // whoops, I don't know here if arg is an lvalue or rvalue
>> }
>> };
>
> Write the return statement. Is it
>
> return make_foo()( arg );
>
> ?

I clearly should have fleshed this out a bit more. Consider something
like a fusion::make_tuple function object

struct make_tuple
{
     template<typename Sig> struct result;

     template<typename This, typename Arg>
     struct result< This( Arg ) >
     {
         typedef tuple< Arg > type;
     };

     // This is wrong!
     template<typename Arg>
     typename result< make_tuple( Arg const & ) >::type
     operator ()( Arg const &arg )
     {
         return tuple< Arg const & >( arg );
     }
};

This is wrong because make_tuple(1) will cause the resulting tuple to
hold on to a dangling reference. But had it been called as:

     int const i = 0;
     make_tuple( i );

... then it's correct. The problem here is that make_tuple::operator()
can't know whether it was passed an lvalue or an rvalue, so it doesn't
know how to compute the return type. In contrast, the result<> template
knows, because you can explicitly specify lvalue or rvalue as:

   // lvalue:
   result_of< make_tuple( int const & ) >::type

   // rvalue:
   result_of< make_tuple( int ) >::type

It is for this reason that Fusion's make_tuple() function (it doesn't
have a function object -- I consider that a defect) recognizes
reference_wrapped objects to be lvalues. There's no other way to
distinguish them in C++03.

-- 
Eric Niebler
Boost Consulting
www.boost-consulting.com

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