Reputation: 515
Lets say we have the following class:
class Foo
{
public:
Foo() { _bar=new Bar };
Foo(const Foo &right) { _bar=new Bar(right.bar); };
Foo(Foo &&right) { _bar=right._bar; right.bar=new Bar(); };
~Foo() { delete _bar; }
Foo &operator=(const Foo &right) { _bar->opertor=(right.bar); return *this;}
Foo &operator=(Foo &&right) { std::swap(_bar, right._bar); return *this;}
void func() { _bar->test=1 };
private:
Bar *_bar;
};
is it legitimate to alter it to the following and expect the end user to know that after the move is performed that the rvalue is no longer valid (in that calling anything other than the assignment operator could crash)?
class Foo
{
public:
Foo() { _bar=new Bar };
Foo(const Foo &right) { _bar=new Bar(right.bar); };
Foo(Foo &&right) { _bar=right._bar; right.bar=nullptr; };
~Foo() { if(_bar != nullptr) delete _bar; }
Foo &operator=(const Foo &right)
{
if(_bar == nullptr)
_bar=new Bar();
_bar->opertor=(right.bar);
return *this;
}
Foo &operator=(Foo &&right)
{
if(_bar != nullptr)
delete _bar;
_bar=right._bar;
right._bar=nullptr;
return *this;
}
void func() { _bar->test=1 };
private:
Bar *_bar;
};
my concern comes from the fact that func (and all other functions in the class) assume that _bar exists.
Upvotes: 2
Views: 155
Reputation: 218770
You should document for your Foo
what preconditions each member function has, for example:
void Foo::func();
Requires:
*this
is not in a moved-from state.
Then you're good to go for any of your clients that need to use Foo::func()
, unless...
If you use Foo
with someone else's library that requires func()
, and does not document something along the lines of "except when in a moved-from state", then you're out of luck.
For example: Let's say that your Foo
has a operator<
like this:
bool operator<(const Foo& x, const Foo& y);
Requries: Neither
x
nory
may be in a moved-from state.
Now if you do something like:
std::vector<Foo> v;
// ... fill v
std::sort(v.begin(), v.end()); // oops!
The last line above requires Foo
to be LessThanComparable
whether or not Foo
is in a moved-from state. And this is true of all of the generic code in the std::lib. The requires clauses for std code apply to user-supplied code and do not make exceptions for moved-from objects.
However, if your operator<
works when either argument is in a moved-from state, then the call to std::sort
is fine. This is true even if func()
still has the requirement that Foo
not be in a moved-from state. This is because std::sort
doesn't require func()
to work at all.
So in summary, it all depends on what other code your Foo
interacts with. Maybe it is ok, and maybe it is not. Document what Foo
does, and understand the requirements of the code you use Foo
with.
Upvotes: 0
Reputation: 1635
In principle it may become invalid, though you might want consider leaving it in an assignable state (which your original implementation, hence edited, did not do). This would follow the policy of the standard library, which says:
Unless otherwise specified, all standard library objects that have been moved from are placed in a valid but unspecified state. That is, only the functions without preconditions, such as the assignment operator, can be safely used on the object after it was moved from
I'd recommend reimplementing the assignment operator such that it swaps "this" object with a newly constructed one. This is generally a good way to avoid introducing incorrect behaviour when implementing assignments.
Upvotes: 7
Reputation: 141586
A moved-from object is supposed to be in a valid but unspecified state. Note that this is a recommendation but not an absolute requirement of the standard.
Your second code will break if a normal operation is performed on it afterwards (specifically, the copy-assignment operator).
If _bar == nullptr
is a valid state then your copy-assignment operator is bugged; if it is not a valid state then I would say your move-constructor is bugged.
NB. In the second code, the if
check in the destructor is redundant, as it is legal to delete a null pointer.
Upvotes: 6
Reputation: 10425
The second version is more idiomatic. After an object is moved from it is assumed it will no longer be used.
The check for nullptr
before calling delete
on the pointer is unnecessary as it is defined in the standard to do nothing.
If the user wishes to use the moved from object, it is his job to make sure it is in a valid state (i.e. assigning from a valid object).
Upvotes: 0
Reputation: 206607
is it legitimate to alter it to the following and expect the end user to know that after the move is performed that the rvalue is no longer valid?
You should certainly do that. An rvalue reference is not supposed be kept in an usable state. The idea behind the move constructors and move assignment operators is that you are moving the useful data from the object. I wouldn't use no longer valid to describe it though. It is still a valid object from a C++ point of view just like nullptr
is a valid pointer.
Upvotes: 0