
21 Apr
2010
21 Apr
'10
6:30 a.m.
Chad Nelson wrote: > On 04/20/2010 11:56 PM, Jeffrey Hellrung wrote: > >>> I've been studying Boost.Move, with an eye toward using it to >>> incorporate move semantics into the XInt library. Most of it looks >>> pretty straightforward, but I don't understand the reason for the >>> BOOST_COPY_ASSIGN_REF macro. If you already have a copy-constructor that >>> takes a reference, what does it give you? > > (Sorry, I meant to write operator=, not copy-constructor, there.) > >> That's why it's called BOOST_COPY_*ASSIGN*_REF. You use it to define >> the copy assignment operator. It expands to const T& if >> BOOST_HAS_RVALUE_REFS, and const rv<T>& otherwise. Boost.Move, in the >> absence of true rvalue references, depends on copy elision to >> efficiently construct from rvalues (and reasonably so), so there's no >> need to do anything special with the copy/move constructor combination. > > I'm obviously missing some key concept, because very little of that > parses. I don't know of any efficient way to determine what that concept > might be, so here are some possibly-nonsensical questions to try to > scare it into view: > > - - How is the "copy assignment operator" different from the normal > operator=(const T&)? > > - - *Why* does it expand to const T& if if BOOST_HAS_RVALUE_REFS and const > rv<T>& otherwise? rv<T> is an rvalue representation, if I understand > correctly, and that isn't equivalent to const T&, is it? > > - - What undesirable thing(s) would happen if you ignored > BOOST_COPY_ASSIGN_REF and simply created a normal operator=(const T&)? If you look at the definition of the move-enabling macros, you'll see that a move-enabled class (under C++03) has the following member functions: operator rv<T>&(); // C1 operator const rv<T>&() const; // C2 T::operator=(T&); // A1 T::operator=(const rv<T>&); // A2 T::operator=(rv<T>&); // A3 The purpose of defining the above array of member functions is to enable rvalues to bind to the move assignment operator (A3) rather than the copy assignment operator (A1 and A2). In fact, that's the hardest part about emulating rvalue references in C++03, and you can still get a good deal of mileage without it. To see how it works, consider which operator= overload gets called when you do T x(...); x = f(); If the return type of f() is T&, its result binds to A1 (directly); if it's const T&, to A2 (via C2); and if it's simply T (so that f() is an rvalue), it (magically) binds to A3 (via C1). If you were to define a T::operator(const T&), then rvalues would bind to this overload (which would be less efficient; we want to move from rvalues, not copy from them). The move-enabling macro defines A1 to simply forward to A2; A2 is what the class author implements. The use of the BOOST_COPY_ASSIGN_REF macro makes it clear what the semantics of A2 ought to be. Of course, none of this is necessary in the presence of rvalue references. Hopefully that answers your questions. >>> Also, how close is it to being official? I'd like to use it, but if >>> it's not going to be accepted any time soon, I can't justify it. >> I can't answer the "how close", but you can still use it in the sense >> that you can adopt its conventions regarding boost::rv, and (if you >> choose) ignore the rest of the library. These conventions are: >> - boost::rv<T>& emulates an rvalue reference (and hence may be safely >> moved from); and >> - a move-enabled class T provides a non-const conversion operator to >> boost::rv<T>& and a const conversion operator to const boost::rv<T>&. > > Let me rephrase the question. The current Boost.Move library has been > discussed on this list since at least January of 2009, and it's still > not approved. So how can I adopt anything regarding boost::rv if the > library that defines boost::rv might still not be available if/when my > library is ready for inclusion into a Boost release? Would I have to > bundle move.hpp with XInt in that case? Or yank out the move stuff > completely? I guess you have a couple options. The goal would be to enable a smooth transition to Boost.Move once accepted: - Include the move.hpp from the boost sandbox, but only (directly) using the definition of boost::rv. For the enabling macros, free functions, etc., use your own implementations (possibly just forwarding to the sandbox implementations) so that changes to the Boost.Move library require minimal retooling on your part. This is what I had in mind originally, but it might not such a good idea as it might cause ODR violations with other libraries taking this approach. In any case, it causes issues when trying to distribute your code, unless you're willing to require users to copy the move library from the sandbox into their boost directory (life could be worse). Perhaps better is... - Clone the Boost.Move library in the sandbox into an inner namespace (xint::detail::rv, xint::detail::move, etc.) and use this "private" rvalue reference emulation framework, in such a way that when Boost.Move is accepted into boost, it will be easy to "lift" your private emulation framework into the boost-standardized one. I know people are generally against the proliferation of private rvalue reference emulation frameworks (myself included), but given the lack of any kind of established convention thus far, it seems like a viable option. Whether either of the above or whatever else you come up with is worth the trouble is up to you. - Jeff