Boost logo

Boost :

Subject: Re: [boost] [move] BOOST_RV_REF(TypeWithoutMoveEmulation) parameters
From: Groke, Paul (paul.groke_at_[hidden])
Date: 2017-05-22 08:44:51


On Monday, 22. Mai 2017 03:21, Gavin Lambert wrote:
> > I just assumed that this was possible by simply passing the argument
> > with boost::move(arg). Apparently it's not though, because boost::move
> > will return a plain T& for types without move emulation.
> > For the time being I have defined my own helper function template
> > "move_any" that will always return a rv<T>&.
> >
> > Is there such a function that I missed? And if not, would you think
> > it'd make sense to provide one (I certainly think it would)?
>
> Apologies for the prior blank reply; accidentally fat-fingered the Send button.
>
> I don't think this makes sense. The Boost.Move emulation library is exactly
> that -- an as-close-to-possible *emulation* of rvalue-reference-based C++11
> move for pre-C++11 compilers. As such it is naturally designed for
> modification of the type-to-be-moved (it requires adding move constructors
> etc as applicable).
>
> Additionally, when actually compiled in C++11 it gracefully upgrades to actual
> rvalue references -- as such the rv<T> class disappears entirely; this is an
> implementation detail and you're not supposed to use it outside of the
> macros in the documented interface of Boost.Move. (Also, I have grave
> doubts that returning one by reference could be correct code, as they're
> typically locally constructed IIRC.)

I'm sorry, I guess that was confusing. My "move_any" function does of course return an rvalue reference in >= C++11 mode. It just doesn't behave differently for types that are not move-enabled in C++98 mode. Regarding returning boost::rv by reference - I didn't invent that, that's exactly what Boost.Move does. It's a necessary hack to allow Boost.Move to work more or less transparently. I simply copied the definition of boost::move sans the "should I return boost::rv<T>& for this type" checks in C++98 mode.

> If you want to write some generic code around swap(), then do that; this
> exists for both C++11 and prior.

I want to write sink functions that can "consume" a value. They're not member functions of the class they're consuming. In C++11 I would use rvalue references for this. In C++98 I don't know any elegant way of expressing this. Of course I could write the function to take a normal lvalue reference, but that's not visible at the call site. If I use BOOST_RV_REF instead, then it becomes visible at the call site because the caller explicitly has to write "move", which IMO is a great improvement. Of course I could use my own helper type for this. But since everything (except the "move_any" function) is already there in Boost.Move, and I also want to fully move-enable some types in the future using Boost.Move (which would require interoperability between my own solution and Boost.Move), I'd like to use Boost.Move/BOOST_RV_REF for it.

As an example, given this C++11 code:

void Foo::StringSink(std::string&& s) { ... }
...
void Bar::SomeEventHandler() {
    this->foo->StringSink(std::move(this->stringMember)); // stringMember is never used after this point
}

I'd like to be able to emulate that in C++98 ala

void Foo::StringSink(BOOST_RV_REF(std::string) s) { ... }
...
void Bar::SomeEventHandler () {
    this->foo->StringSink(boost::move_any(this->stringMember)); // stringMember is never used after this point
}

Boost.Move allows me to do this, but only if the type passed to boost::move() is itself move enabled. Which IMO is an unnecessary restriction.

> Also, be careful with move. It's a shiny new toy so people are excited to play
> with it, but if not used carefully (*especially* with a
> non-C++11 STL) it can easily lead to incorrect behaviour or degraded
> performance. Learn both when and when *not* to use it.

I don't want to come across as confrontational, but ... what makes you think I don't know when and when not to use it?
I'm working with more or less performance critical code here. Not so much that I can warrant writing code that's very hard to write and very hard to understand for the sake of performance. Which is why I don't want to use T& for my sink parameters. But it's definitely code where I don't want to copy e.g. a std::string if I don't have to copy it. And there are quite a few places where I have to "move" strings (or objects containing strings) around, where I cannot use C++98 copy elision stuff.

And regarding implementation details: One of the reasons why I would want this to be implemented in Boost.Move is that I don't want my code to rely on the internals of Boost.Move.

Regards,
Paul Groke


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