Boost logo

Boost :

Subject: Re: [boost] [Boost.Move] A few notes
From: Jeffrey Lee Hellrung, Jr. (jeffrey.hellrung_at_[hidden])
Date: 2012-01-12 18:04:19


On Thu, Jan 12, 2012 at 1:18 PM, Dan Ivy <danivy.mail_at_[hidden]> wrote:

> On Thu, Jan 12, 2012 at 9:16 PM, Jeffrey Lee Hellrung, Jr.
> <jeffrey.hellrung_at_[hidden]> wrote:
> > All fair criticisms. However, I think this is the simplest solution. If
> > BOOST_FWD_REF( T ) expanded to T&, it wouldn't bind to temporaries,
> making
> > the call expression ugly, and unnecessarily so in the cases that true
> > rvalue references are available or the type isn't even movable. In any
> > event, the current forwarding solution is no worse than what we had
> before
> > Boost.Move, so we aren't regressing in any way. If the caller really
> wanted
> > to ensure their object got moved, there is a way to do that (bind the
> > temporary and explicitly move it, or use a macro like BOOST_MOVE below);
>
> I'd hate to repeat myself again, so this is the last time, I promise :)
>

Sorry! I'm probably not explaining things clearly.

I think we misunderstand each other, or at least I don't understand
> you completely. You keep talking about "cases where true
> rvalue-references are avaiable", but I don't see what you mean by
> that?

[...]

> While I prefer uglier but
> movable temporaries, you prefer (I think) neater but possibly
> non-moveable ones. That's cool.
> The regression I was talking about is when the aforementioned C++11
> enthusiastics suddenly need to compile their code on a C++03 compiler,
> I belive they would be upset that their program's efficency is
> compronised, without being given any hint about this during
> compilation (true, this is how it is right now. It doesn't mean that
> it HAS to be like that.), while you think that they'll be happy that
> their code compiles and runs. Again, both are valid opinions.
>

Here's my position:
- Ideally, client code written for both C++11 and C++03 should not have
"ugly" workarounds for C++03 deficiencies, and that's basically what
wrapping all function parameters in some BOOST_MOVE macro (or similar)
would be, a workaround.
- If you really want to guarantee C++11-level performance in C++03 and the
interface can't otherwise guarantee this, and this necessitates some kind
of workaround, then one workaround (wrapping in a BOOST_MOVE macro) is just
about as bad as another (storing a temporary and explicitly moving).

In any case, even if we did end up agreeing that wrapping function
parameters in a BOOST_MOVE macro was desireable, this doesn't entirely
preclude BOOST_FWD_REF( T ) from expanding to T const &; moveable
temporaries would still be moved. The loss is that const-ness of lvalues is
not preserved, but that isn't a common case. Ultimately, I believe the best
solution is to just implement the interface in 2 different ways, depending
on the presence or absence of true rvalue references.

if
> > the callee wanted to be particularly helpful, it would offer more
> overloads
> > to accurately capture moveable temporaries (this can require up to 4 or
> > more overloads and SFINAE, but it's possible).
>
> Interesting. Could you provide a reference or an example?
>

Here's the idea. In C++11, you can just get away with

template< class T > void push_back(T&& x) { /*...*/ forward<T>(x) /*...*/ }

while in C++03, the best simulation of the above behavior I've found is
something like the following.

// captures lvalue-references-to-non-const, lvalue references of moveable
types, and emulated rvalue references
template< class T > void push_back(T&);

// if value_type is moveable, captures value_type temporaries or explicitly
created emulated value_type rvalue_references
// otherwise, captures value_type lvalues and rvalues
typedef call_traits<
    typename add_rvalue_reference< value_type >::type
>::param_type value_rvalue_param_type;
void push_back(value_rvalue_param_type);

// captures any rvalue of moveable type (except of type value_type)
typedef rv_sink< [*] > value_rv_sink_type;
void push_back(rv_sink_type);

// captures any non-moveable temporaries
template< class T > typename boost::enable_if< [**] >::type push_back(T
const &);

For [*], see

http://lists.boost.org/Archives/boost/2011/04/180064.php

(there rv_sink is named genrv) (warning: it's a long read)

For [**], this overload should be disabled if T is convertible to
value_rv_sink_type or value_rvalue_param_type (thus preferring one of the
previous overloads). I didn't want to complicate the presentation too much
by writing that out in MPL gobbledeegook.

Yes, this is a lot of boilerplate on the implementation side, but it should
typically be paid back many times over via a simpler call interface. I
wouldn't even consider the rv_sink overload to be strictly necessary if you
want to relieve yourself of too much implementation burden.

>
> > [...]
> >
> > In truth, I typically forego BOOST_FWD_REF altogether and just write
> > different overload sets for "outward-looking" interfaces depending on
> > BOOST_NO_RVALUE_REFERENCES. In which case BOOST_FWD2_REF is what I end up
> > using more often.
>
> So, it seems that you're already using the same thing I suggest...
>

Well, any function using BOOST_FWD2_REF in its parameter list is only
passed parameters that are lvalues or the result of a boost::forward. So, I
guess I'm using the same thing, but it doesn't sound like it's in the same
context...?

- Jeff


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