Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2008-07-01 16:23:40


Eric Niebler wrote:
> David Abrahams wrote:
>> Eric Niebler wrote:
>>> David Abrahams wrote:
>>>> Eric Niebler wrote:
>>>>
>>>> -- snip 2^10 overloads --
>>>>
>>>> Yeah, that's probably too much, but I'm not sure we have to support
>>>> forwarding for that many arguments. The only thing rvalue forwarding
>>>> buys you (and it can be a big win) is the ability for the wrapped thing
>>>> to make certain optimizations. It's a good idea when you can do
>>>> it. If
>>>> you can't do it, you can fall back to lvalues and in C++03 you've still
>>>> done a credible job.
>>> Even 2^3 overloads has a *very* noticeable effect on compile times.
>>
>> 8 overloads hurts compile times?
>
> Yes, because for each function invocation you have to calculate the
> return type 8x in 8 different ways.

Hmm. Why? Seems to me that only one specialization of the wrapped
class template needs to be instantiated per invocation.
>>>>>> I just think that since you don't have permission to mutate a const
>>>>>> rvalue anyway, forwarding it as a const reference doesn't really hurt
>>>>>> anyone.
>>>>> When I've gotten it wrong, it hurts.
>>>> Maybe you're making assumptions that you really can't afford to make
>>>> (that storing a reference to an lvalue, even when properly detected, is
>>>> a good idea).
>>> Please, I'm not trying to detect lvalue/rvalue-ness!
>>
>> I'm sorry if it sounded like you were being attacked; I was just trying
>> to find an explanation for the hurt you described in the context of my
>> understanding of the meaning of "const rvalue." Maybe where one really
>> gets hurt is in dealing with return values: if you treat const rvalues
>> as const lvalues you *will* dangle,
>
> Yes.
>
>> and if you treat const lvalues as
>> const rvalues you will cause the wrong semantics in way too many common
>> cases.
>
> Yes. I was trying to illustrate those points with the
> fusion::make_vector() example.

I think it's maybe better illustrated as

        template <class F, class T>
        result_of<F(T&)>::type
        forwarder(F const& g, T& x);

If g(x) returns an rvalue, forwarder had better also return an rvalue
or the result will dangle. if g(x) returns a const lvalue and forwarder
does not, you can get silent odd behavior when the value returned from
forwarder turns out to be a different object from the one g(x)
references. Such functions can only work right if result_of returns the
proper "reference-ness."

However, there's no problem AFAICS with a forwarder like this, if it
could be implemented.

 // Made-up syntax follows

        template <class F, class T>
        decltype(g(x))
        forwarder(F const& g, T& x) if (x is an lvalue or const rvalue)
        {
            g( x );
        }

        template <class F, class T>
        decltype(g(move(x)))
        forwarder(F const& g, T x) if (x is a non-const rvalue)
        {
            g( move(x) );
        }

for any sane implementation of F. That's my only point. It's not a big
deal that we can't tell const rvalues from const lvalues where function
arguments are concerned.

>>> I'm letting users opt-in with reference_wrapper.
>>
>> I understand that's the scope you chose. I was only trying to explore
>> whether that facility can usefully be pushed a little further so that we
>> don't lose move semantics inside of forwarded arguments. Well, I guess
>> we don't, if we assume everything's an rvalue by default :-)
>
> I'm not sure if changes are needed to better support move semantics, or
> what they would be. Feedback is welcome.

Basically, if your wrappers copy arguments by default, then it's always
safe for wrapped functions to move from arguments unless they are
reference_wrappers. We could even make move(x) where x is a
reference_wrapper return an lvalue, so it'd be transparent.

-- 
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

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