Standard guarantees about moved-from container [duplicate] - c++

What is the correct way to reuse a moved container?
std::vector<int> container;
container.push_back(1);
auto container2 = std::move(container);
// ver1: Do nothing
//container2.clear(); // ver2: "Reset"
container = std::vector<int>() // ver3: Reinitialize
container.push_back(2);
assert(container.size() == 1 && container.front() == 2);
From what I've read in the C++0x standard draft; ver3 seems to be the correct way, since an object after move is in a
"Unless otherwise specified, such moved-from objects shall be placed
in a valid but unspecified state."
I have never found any instance where it is "otherwise specified".
Although I find ver3 a bit roundabout and would have much preferred ver1, though vec3 can allow some additional optimization, but on the other hand can easily lead to mistakes.
Is my assumption correct?

From section 17.3.26 of the spec "valid but unspecified state":
an object state that is not specified except that the object’s invariants are met and operations on the object behave as specified for its type [ Example: If an object x of type std::vector<int> is in a valid but unspecified state, x.empty() can be
called unconditionally, and x.front() can be called only if x.empty() returns false. —end example ]
Therefore, the object is live. You can perform any operation that does not require a precondition (unless you verify the precondition first).
clear, for example, has no preconditions. And it will return the object to a known state. So just clear it and use it as normal.

The object being in a valid, but undefined state basically means that while the exact state of the object is not guaranteed, it is valid and as such member functions (or non member functions) are guaranteed to work as long as they don't rely on the object having a certain state.
The clear() member function has no preconditions on the state of the object (other than that it is valid, of course) and can therefore be called on moved-from objects. On the other hand for example front() depends on the container being not empty, and can therefore not be called, since it is not guaranteed to be non empty.
Therefore both ver2 and ver3 should both be fine.

I don't think you can do ANYTHING with a moved-from object (except destroy it).
Can't you use swap instead, to get all the advantages of moving but leave the container in a known state?

Related

Is it necessary to reset std::list after been moved?

I've got following code:
std::list some_data;
...
std::list new_data = std::move(some_data);
some_data.clear();
...
The question is whether some_data.clear() is necessary? (for the record, some_data will be reused in the future)
Yes, it's necessary.
Only the std smart pointers are guaranteed to be in a default constructed state after being moved from.
Containers are in an valid, but unspecified state. This means you can only call member functions without preconditions, e.g. clear, that put the object in a fully known state.
The working draft of standard (N4713) states about the state of objects after they are moved from:
20.5.5.15 Moved-from state of library types [lib.types.movedfrom]
1 Objects of types defined in the C++ standard library may be moved from. Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.
20.3.25 [defns.valid]
valid but unspecified state value of an object that is not specified except that the object’s invariants are met and operations on the object behave as specified for its type
The only operations you can safely perform on a moved-from container are those that do not require a precondition. clear has no preconditions. And it will return the object to a known state from which it can be used again.
No it is not necessary to clear the list. It is just reasonable and convenient to do so.
The list will have N elements, each taking an unspecified value, for some N≥0. So if you want to re-poppulate the list, you may in theory assign to the elements that are kept rather than clear and re-insert everything from scratch.
Of course chances that N≠0 are vanishingly small, so in practice clearing is the right choice.
Short answer:
Yes, you should clear it, because the standard doesn't explicitly define in what state the source list is after the move.
Furthermore, always calling clear() when a container gets reused after being moved from is a simple rule that should not cause any significant overhead even when it is redundant.
Longer answer:
As I mentioned in some comments, I can't imagine any legal and sensible implementation for which the source std::list will be anything but an empty std::list after being used as the source for a move constructor (thanks #Lightness Races in Orbit for digging through the standard). Move constructing must happen in linear time, which doesn't allow any per-object operations and AFAIK it is also not allowed to have a small, fixed-size inplace buffer that could be a source of left-over "zombie" elements in the source list.
However, even if you can track down all of that in the standard, you'd still have to document that everytime you omit the clear() in the code and be wary of someone refactoring the code by replacing the std::list with a homegrown container, that doesn't quite full fill the requirements in the standard (e.g. to make use of the small buffer optimization). Also, the previous statement is only valid for move construction. On moveassignment it is absolutely possible that the moved from container will not be empty.
In summary: Even if it was technically correct to not use clear in this particular case, it is imho not worth the mental effort to do it (and if you are in an environment where it is worth it, you are probably tuning your code to a particular version of the standard library and not the standard document anyway).
It rather depends on what you mean by "reused". There are many reuses that totally define the state of the object, with no regard to it's previous state.
The obvious one is assigning to a moved-from object, which occurs in the naive swap
template <typename T>
void swap(T& lhs, T& rhs)
{
T temp = std::move(lhs);
lhs = std::move(rhs); // lhs is moved-from, but we don't care.
rhs = std::move(temp); // rhs is moved-from, but we don't care.
}
If you instead wish to "reuse" by calling some_data.push_back, then yes, you should clear it first

Efficiently pass (and empty) vectors to object's constructor with move [duplicate]

I've been trying to wrap my head around how move semantics in C++11 are supposed to work, and I'm having a good deal of trouble understanding what conditions a moved-from object needs to satisfy. Looking at the answer here doesn't really resolve my question, because can't see how to apply it to pimpl objects in a sensible way, despite arguments that move semantics are perfect for pimpls.
The easiest illustration of my problem involves the pimpl idiom, like so:
class Foo {
std::unique_ptr<FooImpl> impl_;
public:
// Inlining FooImpl's constructors for brevity's sake; otherwise it
// defeats the point.
Foo() : impl_(new FooImpl()) {}
Foo(const Foo & rhs) : impl_(new FooImpl(*rhs.impl_)) {}
Foo(Foo && rhs) : impl_(std::move(rhs.impl_)) {}
Foo & operator=(Foo rhs)
{
std::swap(impl_, rhs.impl_);
return *this;
}
void do_stuff ()
{
impl_->do_stuff;
}
};
Now, what can I do once I've moved from a Foo? I can destroy the moved-from object safely, and I can assign to it, both of which are absolutely crucial. However, if I try to do_stuff with my Foo, it will explode. Before I added move semantics for my definition of Foo, every Foo satisfied the invariant that it could do_stuff, and that's no longer the case. There don't seem to be many great alternatives, either, since (for example) putting the moved-from Foo would involve a new dynamic allocation, which partially defeats the purpose of move semantics. I could check whether impl_ in do_stuff and initialize it to a default FooImpl if it is, but that adds a (usually spurious) check, and if I have a lot of methods it would mean remembering to do the check in every one.
Should I just give up on the idea that being able to do_stuff is a reasonable invariant?
You define and document for your types what a 'valid' state is and what operation can be performed on moved-from objects of your types.
Moving an object of a standard library type puts the object into an unspecified state, which can be queried as normal to determine valid operations.
17.6.5.15 Moved-from state of library types
[lib.types.movedfrom]
Objects of types defined in the C++ standard library may be moved from
(12.8). Move operations may be explicitly specified or implicitly
generated. Unless otherwise specified, such moved-from objects shall
be placed in a valid but unspecified state.
The object being in a 'valid' state means that all the requirements the standard specifies for the type still hold true. That means you can use any operation on a moved-from, standard library type for which the preconditions hold true.
Normally the state of an object is known so you don't have to check if it meets the preconditions for each operation you want to perform. The only difference with moved-from objects is that you don't know the state, so you do have to check. For example, you should not pop_back() on a moved-from string until you have queried the state of the string to determine that the preconditions of pop_back() are met.
std::string s = "foo";
std::string t(std::move(s));
if (!s.empty()) // empty has no preconditions, so it's safe to call on moved-from objects
s.pop_back(); // after verifying that the preconditions are met, pop_back is safe to call on moved-from objects
The state is probably unspecified because it would be onerous to create a single useful set of requirements for all different implementations of the standard library.
Since you are responsible not only for the specification but also the implementation of your types, you can simply specify the state and obviate the need for querying. For example it would be perfectly reasonable to specify that moving from your pimpl type object causes do_stuff to become an invalid operation with undefined behavior (via dereferencing a null pointer). The language is designed such that moving only occurs either when it's not possible to do anything to the moved-from object, or when the user has very obviously and very explicitly indicated a move operation, so a user should never be surprised by a moved-from object.
Also note that the 'concepts' defined by the standard library do not make any allowances for moved-from objects. That means that in order to meet the requirements for any of the concepts defined by the standard library, moved-from objects of your types must still fulfill the concept requirements. This means that if objects of your type don't remain in a valid state (as defined by the relevant concept) then you cannot use it with the standard library (or the result is undefined behavior).
However, if I try to do_stuff with my Foo, it will explode.
Yes. So will this:
vector<int> first = {3, 5, 6};
vector<int> second = std::move(first);
first.size(); //Value returned is undefined. May be 0, may not
The rule the standard uses is to leave the object in a valid (meaning that the object works) but unspecified state. This means that the only functions you can call are those that have no conditions on the current state of the object. For vector, you can use its copy/move assignment operators, as well as clear and empty, and several other operations. So you can do this:
vector<int> first = {3, 5, 6};
vector<int> second = std::move(first);
first.clear(); //Cause the vector to become empty.
first.size(); //Now the value is guaranteed to be 0.
For your case, copy/move assignment (from either side) should still work, as should the destructor. But all other functions of yours have a precondition based on the state of not having been moved from.
So I don't see your problem.
If you wanted to ensure that no instance of a Pimpl'd class could be empty, then you would implement proper copy semantics and forbid moving. Movement requires the possibility of an object being in an empty state.

What constitutes a valid state for a "moved from" object in C++11?

I've been trying to wrap my head around how move semantics in C++11 are supposed to work, and I'm having a good deal of trouble understanding what conditions a moved-from object needs to satisfy. Looking at the answer here doesn't really resolve my question, because can't see how to apply it to pimpl objects in a sensible way, despite arguments that move semantics are perfect for pimpls.
The easiest illustration of my problem involves the pimpl idiom, like so:
class Foo {
std::unique_ptr<FooImpl> impl_;
public:
// Inlining FooImpl's constructors for brevity's sake; otherwise it
// defeats the point.
Foo() : impl_(new FooImpl()) {}
Foo(const Foo & rhs) : impl_(new FooImpl(*rhs.impl_)) {}
Foo(Foo && rhs) : impl_(std::move(rhs.impl_)) {}
Foo & operator=(Foo rhs)
{
std::swap(impl_, rhs.impl_);
return *this;
}
void do_stuff ()
{
impl_->do_stuff;
}
};
Now, what can I do once I've moved from a Foo? I can destroy the moved-from object safely, and I can assign to it, both of which are absolutely crucial. However, if I try to do_stuff with my Foo, it will explode. Before I added move semantics for my definition of Foo, every Foo satisfied the invariant that it could do_stuff, and that's no longer the case. There don't seem to be many great alternatives, either, since (for example) putting the moved-from Foo would involve a new dynamic allocation, which partially defeats the purpose of move semantics. I could check whether impl_ in do_stuff and initialize it to a default FooImpl if it is, but that adds a (usually spurious) check, and if I have a lot of methods it would mean remembering to do the check in every one.
Should I just give up on the idea that being able to do_stuff is a reasonable invariant?
You define and document for your types what a 'valid' state is and what operation can be performed on moved-from objects of your types.
Moving an object of a standard library type puts the object into an unspecified state, which can be queried as normal to determine valid operations.
17.6.5.15 Moved-from state of library types
[lib.types.movedfrom]
Objects of types defined in the C++ standard library may be moved from
(12.8). Move operations may be explicitly specified or implicitly
generated. Unless otherwise specified, such moved-from objects shall
be placed in a valid but unspecified state.
The object being in a 'valid' state means that all the requirements the standard specifies for the type still hold true. That means you can use any operation on a moved-from, standard library type for which the preconditions hold true.
Normally the state of an object is known so you don't have to check if it meets the preconditions for each operation you want to perform. The only difference with moved-from objects is that you don't know the state, so you do have to check. For example, you should not pop_back() on a moved-from string until you have queried the state of the string to determine that the preconditions of pop_back() are met.
std::string s = "foo";
std::string t(std::move(s));
if (!s.empty()) // empty has no preconditions, so it's safe to call on moved-from objects
s.pop_back(); // after verifying that the preconditions are met, pop_back is safe to call on moved-from objects
The state is probably unspecified because it would be onerous to create a single useful set of requirements for all different implementations of the standard library.
Since you are responsible not only for the specification but also the implementation of your types, you can simply specify the state and obviate the need for querying. For example it would be perfectly reasonable to specify that moving from your pimpl type object causes do_stuff to become an invalid operation with undefined behavior (via dereferencing a null pointer). The language is designed such that moving only occurs either when it's not possible to do anything to the moved-from object, or when the user has very obviously and very explicitly indicated a move operation, so a user should never be surprised by a moved-from object.
Also note that the 'concepts' defined by the standard library do not make any allowances for moved-from objects. That means that in order to meet the requirements for any of the concepts defined by the standard library, moved-from objects of your types must still fulfill the concept requirements. This means that if objects of your type don't remain in a valid state (as defined by the relevant concept) then you cannot use it with the standard library (or the result is undefined behavior).
However, if I try to do_stuff with my Foo, it will explode.
Yes. So will this:
vector<int> first = {3, 5, 6};
vector<int> second = std::move(first);
first.size(); //Value returned is undefined. May be 0, may not
The rule the standard uses is to leave the object in a valid (meaning that the object works) but unspecified state. This means that the only functions you can call are those that have no conditions on the current state of the object. For vector, you can use its copy/move assignment operators, as well as clear and empty, and several other operations. So you can do this:
vector<int> first = {3, 5, 6};
vector<int> second = std::move(first);
first.clear(); //Cause the vector to become empty.
first.size(); //Now the value is guaranteed to be 0.
For your case, copy/move assignment (from either side) should still work, as should the destructor. But all other functions of yours have a precondition based on the state of not having been moved from.
So I don't see your problem.
If you wanted to ensure that no instance of a Pimpl'd class could be empty, then you would implement proper copy semantics and forbid moving. Movement requires the possibility of an object being in an empty state.

Reusing a moved container?

What is the correct way to reuse a moved container?
std::vector<int> container;
container.push_back(1);
auto container2 = std::move(container);
// ver1: Do nothing
//container2.clear(); // ver2: "Reset"
container = std::vector<int>() // ver3: Reinitialize
container.push_back(2);
assert(container.size() == 1 && container.front() == 2);
From what I've read in the C++0x standard draft; ver3 seems to be the correct way, since an object after move is in a
"Unless otherwise specified, such moved-from objects shall be placed
in a valid but unspecified state."
I have never found any instance where it is "otherwise specified".
Although I find ver3 a bit roundabout and would have much preferred ver1, though vec3 can allow some additional optimization, but on the other hand can easily lead to mistakes.
Is my assumption correct?
From section 17.3.26 of the spec "valid but unspecified state":
an object state that is not specified except that the object’s invariants are met and operations on the object behave as specified for its type [ Example: If an object x of type std::vector<int> is in a valid but unspecified state, x.empty() can be
called unconditionally, and x.front() can be called only if x.empty() returns false. —end example ]
Therefore, the object is live. You can perform any operation that does not require a precondition (unless you verify the precondition first).
clear, for example, has no preconditions. And it will return the object to a known state. So just clear it and use it as normal.
The object being in a valid, but undefined state basically means that while the exact state of the object is not guaranteed, it is valid and as such member functions (or non member functions) are guaranteed to work as long as they don't rely on the object having a certain state.
The clear() member function has no preconditions on the state of the object (other than that it is valid, of course) and can therefore be called on moved-from objects. On the other hand for example front() depends on the container being not empty, and can therefore not be called, since it is not guaranteed to be non empty.
Therefore both ver2 and ver3 should both be fine.
I don't think you can do ANYTHING with a moved-from object (except destroy it).
Can't you use swap instead, to get all the advantages of moving but leave the container in a known state?

Does moving leave the object in a usable state?

Say I have two vectors and I move one unto the other, v1 = std::move(v2); will v2 still be in a usable state after this?
From n3290, 17.6.5.15 Moved-from state of library types [lib.types.movedfrom]
Objects of types defined in the C++ standard library may be moved from (12.8). Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.
Since the state is valid, this means you can safely operate on v2 (e.g. by assigning to it, which would put it back to a known state). Since it is unspecified however, it means you cannot for instance rely on any particular value for v2.empty() as long as it is in this state (but calling it won't crash the program).
Note that this axiom of move semantics ("Moved from objects are left in a valid but unspecified state") is something that all code should strive towards (most of the time), not just the Standard Library components. Much like the semantics of copy constructors should be making a copy, but are not enforced to.
No, it is left in an unspecified state.
Excerpt from open-std-org article -
.. move() gives its target the value of its argument, but is not obliged to preserve the value of its source. So, for a vector, move() could reasonably be expected to leave its argument as a zero-capacity vector to avoid having to copy all the elements. In other words, move is a potentially destructive read.
If you want to use v2 after the move, you will want to do something like:
v1 = std::move(v2);
v2.clear();
At this point, v1 will have the original contents of v2 and v2 will be in a well-defined empty state. This works on all of the STL containers (and strings, for that matter), and if you are implementing your own classes that support move-semantics, you'll probably want to do something similar.
If your particular implementation of the STL actually DOES leave the object in an empty state, then the second clear() will be essentially a no-op. In fact, if this is the case, it would be a legal optimization for a compiler to eliminate the clear() after the move.