
On Thu, Jan 12, 2012 at 1:18 PM, Dan Ivy <danivy.mail@gmail.com> wrote:
On Thu, Jan 12, 2012 at 9:16 PM, Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung@gmail.com> 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