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.
In C++98 you can pass a parameter that is not movable (meaning, implements
internal boost::rv facility) to boost::move it will just send a reference
to it.
If the type doesn't implements boost::rv, I think you're stuck with copies
and such, so it's not an unnecessary restriction.
As for stl containers such as std::string or std::vector, you could use
boost::container::string (vector, etc.), they emulate move semantics for
C++98 compilers, and as far as I used them in my applications, in most
cases they outperform stl containers.
But the main drawback that comes with this workaround is the
interoperability with other APIs that use std::string (or other) because
they don't know boost::container, so you have to write more code that
handle this use case.
Regards
Danny
2017-05-22 10:44 GMT+02:00 Groke, Paul via Boost
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
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/ mailman/listinfo.cgi/boost