|
Boost : |
Subject: Re: [boost] [interprocess] default constructors
From: Ion Gaztañaga (igaztanaga_at_[hidden])
Date: 2008-11-30 15:50:39
Mathias Gaunard wrote:
> Ion Gaztañaga wrote :
>
>> I don't think move should be implemented as swap, because the resource
>> (shared memory) is still there floating around:
>
> Indeed, but that is the only way to really keep the strong invariant of
> one-way construction (which certainly is useful, since it allows optimal
> object performance by avoiding tests for "emptiness" and leads to safer
> code since operations on empty objects may not happen).
Yes but this might be surprising to the user, specially when objects are
stored in containers and the user does not "see" the move operation.
> Ideally, we would want destructive move semantics (move semantics where
> the moved-from object is destructed when move is performed) but those
> are not what C++0x provides, unfortunately.
Destructive move semantics would be nice to have, but I think they are
complementary to non-destructive ones and ideally I would like to have
compiler support to detect "dangling references" (moved, thus destroyed
objects) at compile time.
>> //other_shm will be in default-constructed state
>> shm = move(other_shm)
>
> Why must that state be the default-constructed one?
> It could just be an unspecified private state than can only be reached
> from a move, and where only the minimum required operations, such as
> destruction, are valid.
Correct. It's a design decision.
> However, that means potential overhead for those operations. And I
> personally still do not know what operations are required to be valid on
> moved-from objects, since that is badly specified, which means in the
> worst case you have to support all assignment and copying scenarios
> from/to moved-from objects.
I think there is no overhead. If a private state can be achieved
representing "empty object" you can make it public.
> There is overhead, but only less than with allowing two-phase
> construction, unless the rollback semantics are hard to maintain on
> assignment due to exception safety issues (you might also choose not to
> support them here and fall back to the moved state on failure).
I understand your points. And I appreciate the one-construction
invariant. But that invariant will be broken with move semantics
(because they are non-destructive) and you can have non-operational
objects. In practice, I've found default constructors useful for
non-copyable elements, because they make some code easier without adding
any penalty (because the internal "moved" state must exist) and the
default constructor makes your code easier to write when branches are
around. Imagine a class that has a movable-only type:
class MyClass
{
private:
movable_only mo_;
};
If I want to implement two-phase construction (for whatever reason) I
need to use optional or use heap allocation (in some situations dynamic
memory is not available, in embedded systems or when you want to place
the object inside shared memory). I also need to initialize the member
in the constructor argument list, which is really annoying if I need to
do some non-trivial calculations to obtain the data to construct the member:
MyClass ()
: mo_(/*non-trivial code should go here to initialize mo_*/)
{
//...
};
Of course, I could call an static member that returns some data, but I
would need to repeat that for every argument or mo_). With default
constructors I can do this:
MyClass ()
: mo_()
{
//Non trivial code...
//...
//I have all the data to correctly construct mo_
mo_ = move(movable_only(...));
};
Another option would be to call an static function that returns a
movable_only object:
MyClass ()
: mo_(static_create_movable_only_from_data(...))
{};
but this can be more costly than the default constructed case, because I
need to forward all the data to the static function.
I'm sure you can workaround these problems, but I think they complicate
coding.
Regards,
Ion
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk