Saturday, 10 August 2013

What should the default constructor do in a RAII class with move semantics?

What should the default constructor do in a RAII class with move semantics?

Move semantics are great for RAII classes. They allow one to program as if
one had value semantics without the cost of heavy copies. A great example
of this is returning std::vector from a function. Programming with value
semantics however means, that one would expect types to behave like
primitive data types. Those two aspects sometimes seem to be at odds.
On the one hand, in RAII one would expect the default constructor to
return a fully initialized object or throw an exception if the resource
acquisition failed. This guarantees that any constructed object will be in
a valid and consistent state (i.e. safe to use).
On the other hand, with move semantics there exists a point when objects
are in a valid but unspecified state. Similarly, primitive data types can
be in an uninitialized state. Therefore, with value semantics, I would
expect the default constructor to create an object in this valid but
unspecified state, so that the following code would have the expected
behavior:
// Primitive Data Type, Value Semantics
int i;
i = 5;
// RAII Class, Move Semantics
Resource r;
r = Resource{/*...*/}
In both cases, I would expect the "heavy" initialization to occur only
once. I am wondering, what is the best practice regarding this? Obviously,
there is a slight practical issue with the second approach: If the default
constructor creates objects in the unspecified state, how would one write
a constructor that does acquire a resource, but takes no additional
parameters? (Tag dispatching comes to mind...)

No comments:

Post a Comment