I am not quite sure what the way to go here is; if I have a class with pointer members and a move constructor that sets the pointer members to nullptr
and the move-functions are the only function that can set the pointer members to 0, do I then need to put tests for zero everywhere I dereference any of the members, as the object might have been passed to a move constructor?
for example, a function
unisgned size()
{
return memberpointer->size();
}
would become
unisgned size()
{
if memberpointer == nullptr
return 0;
return memberpointer->size();
}
Otherwise of course this would give a segfault if size()
is called on a moved object. Would that be the fault of the user or my fault for not building a null-test? If the user does not access moved object before re-initialising it, this could not happen as long as I put a null-test in the copy assignment operator:
C& operator=(C& src)
{
if (this != &src)
{
if (memberpointer == nullptr)
init();
*memberpointer = *src.memberpointer;
}
return *this;
}
Putting test for null everywhere would reduce performance, which would be unnecessary if the user knew that accessing a moved and not re-initialized object is undefined behavior. What is the way to go about this?
No, there's no need for all those tests, any more than if you didn't have move semantics at all. If it's illegal to use your object before calling some sort of initialization function, then you should also consider it illegal to use the object after moving. Your object should be left in a state that can be properly destructed, but there are no other rules specifying what that state should be.
For example:
Foo foo;
foo.Init(); // required
// foo is now allowed to be used
Foo bar = std::move( foo );
// No guarantees on whether foo is now initialised or not.
Move semantics do not stipulate what state the object moved from will be in after the move. It only requires the object is in some internally consistent state. So, your move constructor or assignment operator is perfectly entitled to swap the members if it wishes... Provided all the other internal states of the object are consistent with that. That is, it's perfectly okay to have this situation:
Foo foo, bar;
foo.Init();
bar.Init();
bar = std::move( foo );
// foo and bar contents now swapped.
// caveat: you should not use foo again
However, you must not expect this. The rule is that after a move, you should not attempt to use the object again.