
On 1/21/2013 8:39 AM, Antony Polukhin wrote:
Current implementation of recursive_wrapper move constructor is not optimal:
recursive_wrapper<T>::recursive_wrapper(recursive_wrapper&& operand) : p_(new T(std::move(operand.get()) )) { }
During descussion were made following proposals:
I: Leave it as is - bad performance - can not provide noexcept guarantee
II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations + good performance + provides noexcept guarantee for move constructor + optimization will be used even if varinat has no type with trivial default constructor + easy to implement - triggers an assert when user tries to reuse moved object - adds an empty state to the recursive_wrapper
III: Make recursive_wrapper and variant cooperate, enable move for varinat in the presence of recursive_wrappers only when there is at least one type that is nothrow-default-constructible, regardless of whether it's current. It is easyer to understand by example: typedef variant<int, recursive_wrapper<foo>> V; V v1( std::move(v2) ); This move-constructs v1 from v2 and leaves int() into v2. + good performance + provides noexcept guarantee for rcursive_wrapper + does not adds an empty state - optimization won't trigger if varinat has no type with trivial default constructor - hard to implement - user may be obscured by the fact, that v2 from the example now contains int
IV: After move away, delay construction of type held by recursive_wrapper till it will be be required. +/- good performance? but more checks + provides noexcept guarantee for rcursive_wrapper + does not adds an explicit empty state + optimization will be used even if varinat has no type with trivial default constructor +/- not hard to implement --- additional requirement for type T of recursive_wrapper: it must be default constructible - less obvious behavior for user (for example get() function would construct values, allocate memory and can possibly throw)
The main issue seems to be simply this: are guarantees ( invariants ) for an object of a class meant to cover moved from objects of that class ? All of the choices which you have specified, which popularly boils down to II or III, involves this question. The choice of II answers No to the question above while the choice of III answers Yes to the question above. But the issue is larger than this particular case and really needs comment from someone who understands the C++ standard, rvalue references, and moved objects beyond what is written in the C++ standard document. For instance I read in Lippman's "C++ Primer 5th Edition" that the only things one should be able to do with a moved from object is destroy or assign new values to it. This implies to me that a moved from object should not really be used in any way unless one can repopulate its values. IMO this supports the choice of II and that a guarantee for a moved from object should no longer hold, since it is hopeless to use that object unless one assigns something to it. But perhaps Lippman's POV is not really that of the C++ standard committee regarding the usefulness of moved from objects. Or perhaps, despite the fact that the moved from object should not be used unless an appropriate value(s) is reassigned to it, guarantees on a moved from object should still hold. I really do not know, but it is important to clarify what the disagreement is about and that it goes well beyond the present case.