
Eric Niebler wrote:
[...] However, I might prefer a more conservative approach whereby users must opt-in to the memcpy implementation instead of making it the default.
In fact I arrived at this same conclusion after I realised the bitwise approach was not as general as I thought. The library documentation should contain some lines like the following in its introduction: "In the general case, the user of this library will provide template specializations of underlying_type_traits and move_raw. Their type can then take advantage of algorithms that use the move_raw mechanism. As a special case, if you are sure that your type does not depend on the memory location of an object for the validity of its value (see notes below), you can opt-in for the included bitwise implementation. In that case you get move_raw and the underlying type basically for free." After which the "notes below" would provide a thorough explanation on how to recognize problematic types. Nevin Liber wrote:
How often is this thing needed? The only times you need this is when the compiler generated move constructor/assignment operators either are deleted or are doing the wrong thing. Is that really the time you don't want people thinking about how to correctly write swap (or better, just write the correct move constructor and assignment operator).
How about compilers that don't generate move constructors and move assignments at all because they don't support them? And what about types for which move_raw is more efficient than source-validity- preserving move (i.e. any type with a nontrivial default constructor)?
Even examining the implementation for all your member variables isn't enough. The boost::function which holds a boost::bind(..., this, ...) where the function object is stored inside the boost::function object itself may now exhibit different semantics than when the function object it is holding is in the heap. Ugh.
Users should simply be warned that if their type has member data that were implemented by a third party and which are initialized with a pointer to (a part of) the main object, they should assume their type to depend on the object's memory location for its validity.
Way back when, C++ did bitwise copying for the compiler generated copy operations, and was changed to member wise copying for good reason. I really don't want to go back to that world.
Let me reassure you that move_raw is not inherently about bitwise copying; you can do exactly the same with memberwise copying but that requires the author of the type to provide their own implementation.
The problem is, you are adding in a dependency on the *implementation*, not the interface of objects. Consider:
struct W { X x; Y y; Z z; };
1. What are the requirements on implementations of X, Y and Z? Is trivially copyable enough? How about trivially copyable and isn't moveable (unless you want to second guess the move constructor)? Of course, if it is trivially copyable and not moveable, there is probably a reason...
The requirement is that it would be meaningful for W to be moveable, and hence the same must apply to X, Y an Z. I'm assuming that users are not interested in move_raw if value-permuting algorithms like swap make no sense for their type. Note that copying is a special case of moving; if copying makes sense for a type and the type is mutable, then move_raw also makes sense. I don't see how a type could be mutable and trivially copyable but not moveable, except for a sheer lack of implementation of move operators.
2.. How are you going to enforce that?
I don't feel obliged to enforce this. If users insist on trying to move around values which are immutable or non-movable, they have my blessing. Sebastian Redl wrote:
On 21.08.2011 21:23, Julian Gonggrijp wrote:
Dear all,
I think the set of types with which bitwise move_raw will yield undefined behaviour can be sharply defined: The standard already does. Undefined behavior is a notion of the standard, not of a particular implementation. The standard says that this technique works only for PODs (trivially copyable types in 0x), nothing else.
Doesn't the standard say that bitwise copying of non-PODs is undefined behaviour /because there are cases where bitwise copying will give you an invalid result/? Have we not identified the cases for which an invalid result will be obtained? Can we therefore not maintain -- making use of the semantical definition of move_raw and restricting ourselves to the set of unproblematic types -- that bitwise copying is a safe implementation of move_raw even though in a very strict juridical sense it may be undefined behaviour?
There is no such thing as an underlying type. It's impossible to realise in current C++, but conceptually it exists.
Conceptually, if you think of classes as "aggregates and then some", there is the underlying aggregate.
I'm not thinking of classes in that way. The underlying type is something different from an "underlying aggregate" (although I realise I have given that impression in my first email; my apologies). Instead, the underlying type is defined by its semantic relation to the 'overlying type' as I have also stated in my reply to Mathias Gaunard (http://lists.boost.org/Archives/boost/2011/08/184913.php): The underlying type of T is a POD which can store the state of an instance of T. So far, it seems that those who have read Stepanov's paper are more positive about the possible value of my proposal than those who didn't read it. This seems to confirm that Stepanov is still better at explaining the value of move_raw than I am. Therefore I invite those who haven't read the paper yet to still do so. Only lectures 4 and 5 are required; it's only 11 pages with some trailing source code that you can skip. It's a very enjoyable read and I almost dare to promise you that you'll want to read the rest of the paper as well. :-) The URL: http://www.stepanovpapers.com/notes.pdf (If you happen to find something inside the paper that was missing in my own argumentation so far, please let me know; such feedback would be invaluable for future documentation of an underlying type library.) -Julian