Boost logo

Boost :

From: Eric Niebler (eric_at_[hidden])
Date: 2008-07-01 18:24:05


David Abrahams wrote:
>
> 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."

Correct. And the question raised in the "result_of and C++03" thread
was: is the "reference-ness" of of result_of<F(T&)>::type allowed to be
influenced by the "reference-ness" of the arguments in the function
type. That is, what do these mean, are they different and if so, how?

   result_of<F(T)>::type // 1
   result_of<F(T&)>::type // 2
   result_of<F(T const&)>::type // 3

There be dragons there in C++03 because there's no way in C++03 to
overload a function on T, T& and T const&. The only workable solution
I've found so far is that these all mean the same thing: the result of
calling F with an rvalue of type T, unless T is a reference_wrapper<U>,
in which case, it's the result of calling F with an lvalue of type U.

If your reaction is "yuk" then I agree, but I haven't yet thought of
something better. Now, you might say that we can detect case (2) in
C++03 and make it mean "lvalue of type T", but for functions that take N
arguments, that leads us to 2^N overloads, and 2^N return type
calculations, which I'm trying to avoid.

> 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.

Hm, I *think* I get it.

>>>> 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,

They don't. They could, but I don't think they should, do you?

> 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.

I'm not seeing a win here, at least not for C++03. Little help?

-- 
Eric Niebler
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