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
Related
In future standards of C++, we will have the concept of "trivial relocatability", which means we can simply copy bytes from one object to an uninitialized chunk of memory, and simply ignore/zero out the bytes of the original object.
this way, we imitate the C-style way of copying/moving objects around.
In future standards, we will probably have something like std::is_trivially_relocatable<type> as a type trait. currently, the closest thing we have is std::is_pod<type> which will be deprecated in C++20.
My question is, do we have a way in the current standard (C++17) to figure out if the object is trivially relocatable?
For example, std::unique_ptr<type> can be moved around by copying its bytes to a new memory address and zeroing out the original bytes, but std::is_pod_v<std::unique_ptr<int>> is false.
Also, currently the standard mandate that every uninitialized chunk of memory must pass through a constructor in order to be considered a valid C++ object. even if we can somehow figure out if the object is trivially relocatable, if we just move the bytes - it's still UB according to the standard.
So another question is - even if we can detect trivial relocatability, how can we implement trivial relocation without causing UB? simply calling memcpy + memset(src,0,...) and casting the memory address to the right type is UB.
`
Thanks!
The whole point of trivial-relocatability would seem to be to enable byte-wise moving of objects even in the presence of a non-trivial move constructor or move assignment operator. Even in the current proposal P1144R3, this ultimately requires that a user manually mark types for which this is possible. For a compiler to figure out whether a given type is trivially-relocatable in general is most-likely equivalent to solving the halting problem (it would have to understand and reason about what an arbitrary, potentially user-defined move constructor or move assignment operator does)…
It is, of course, possible that you define your own is_trivially_relocatable trait that defaults to std::is_trivially_copyable_v and have the user specialize for types that should specifically be considered trivially-relocatable. Even this is problematic, however, because there's gonna be no way to automatically propagate this property to types that are composed of trivially-relocatable types…
Even for trivially-copyable types, you can't just copy the bytes of the object representation to some random memory location and cast the address to a pointer to the type of the original object. Since an object was never created, that pointer will not point to an object. And attempting to access the object that pointer doesn't point to will result in undefined behavior. Trivial-copyabibility means you can copy the bytes of the object representation from one existing object to another existing object and rely on that making the value of the one object equal to the value of the other [basic.types]/3.
To do this for trivially-relocating some object would mean that you have to first construct an object of the given type at your target location, then copy the bytes of the original object into that, and then modify the original object in a way equivalent to what would have happened if you had moved from that object. Which is essentially a complicated way of just moving the object…
There's a reason a proposal to add the concept of trivial-relocatability to the language exists: because you currently just can't do it from within the langugage itself…
Note that, despite all this, just because the compiler frontend cannot avoid generating constructor calls doesn't mean the optimizer cannot eliminate unnecessary loads and stores. Let's have a look at what code the compiler generates for your example of moving a std::vector or std::unique_ptr:
auto test1(void* dest, std::vector<int>& src)
{
return new (dest) std::vector<int>(std::move(src));
}
auto test2(void* dest, std::unique_ptr<int>& src)
{
return new (dest) std::unique_ptr<int>(std::move(src));
}
As you can see, just doing an actual move often already boils down to just copying and overwriting some bytes, even for non-trivial types…
Author of P1144 here; somehow I'm just seeing this SO question now!
std::is_trivially_relocatable<T> is proposed for some-future-version-of-C++, but I don't predict it'll get in anytime soon (definitely not C++23, I bet not C++26, quite possibly not ever). The paper (P1144R6, June 2022) ought to answer a lot of your questions, especially the ones where people are correctly answering that if you could already implement this in present-day C++, we wouldn't need a proposal. See also my 2019 C++Now talk.
Michael Kenzel's answer says that P1144 "ultimately requires that a user manually mark types for which [trivial relocation] is possible"; I want to point out that that's kind of the opposite of the point. The state of the art for trivial relocatability is manual marking ("warranting") of each and every such type; for example, in Folly, you'd say
struct Widget {
std::string s;
std::vector<int> v;
};
FOLLY_ASSUME_FBVECTOR_COMPATIBLE(Widget);
And this is a problem, because the average industry programmer shouldn't be bothered with trying to figure out if std::string is trivially relocatable on their library of choice. (The annotation above is wrong on 1.5 of the big 3 vendors!) Even Folly's own maintainers can't get these manual annotations right 100% of the time.
So the idea of P1144 is that the compiler can just take care of it for you. Your job changes from dangerously warranting things-you-don't-necessarily-know, to merely (and optionally) verifying things-you-want-to-be-true via static_assert (Godbolt):
struct Widget {
std::string s;
std::vector<int> v;
};
static_assert(std::is_trivially_relocatable_v<Widget>);
struct Gadget {
std::string s;
std::list<int> v;
};
static_assert(!std::is_trivially_relocatable_v<Gadget>);
In your (OP's) specific use-case, it sounds like you need to find out whether a given lambda type is trivially relocatable (Godbolt):
void f(std::list<int> v) {
auto widget = [&]() { return v; };
auto gadget = [=]() { return v; };
static_assert(std::is_trivially_relocatable_v<decltype(widget)>);
static_assert(!std::is_trivially_relocatable_v<decltype(gadget)>);
}
This is something you can't really do at all with Folly/BSL/EASTL, because their warranting mechanisms work only on named types at the global scope. You can't exactly FOLLY_ASSUME_FBVECTOR_COMPATIBLE(decltype(widget)).
Inside a std::function-like type, you're correct that it would be useful to know whether the captured type is trivially relocatable or not. But since you can't know that, the next best thing (and what you should do in practice) is to check std::is_trivially_copyable. That's the currently blessed type trait that literally means "This type is safe to memcpy, safe to skip the destructor of" — basically all the things you're going to be doing with it. Even if you knew that the type was exactly std::unique_ptr<int>, or whatever, it would still be undefined behavior to memcpy it in present-day C++, because the current standard says that you're not allowed to memcpy types that aren't trivially copyable.
(Btw, technically, P1144 doesn't change that fact. P1144 merely says that the implementation is allowed to elide the effects of relocation, which is a huge wink-and-nod to implementors that they should just use memcpy. But even P1144R6 doesn't make it legal for ordinary non-implementor programmers to memcpy non-trivially-copyable types: it leaves the door open for some compiler to implement, and some library implementation to use, a __builtin_trivial_relocate function that is in some magical sense distinguishable from a plain old memcpy.)
Finally, your last paragraph refers to memcpy + memset(src,0,...). That's wrong. Trivial relocation is tantamount to just memcpy. If you care about the state of the source object afterward — if you care that it's all-zero-bytes, for example — then that must mean you're going to look at it again, which means you aren't actually treating it as destroyed, which means you aren't actually doing the semantics of a relocate here. "Copy and null out the source" is more often the semantics of a move. The point of relocation is to avoid that extra work.
An example move constructor implementation from a C++ course I’m taking looks a bit like this:
/// Move constructor
Motorcycle::Motorcycle(Motorcycle&& ori)
: m_wheels(std::move(ori.m_wheels)),
m_speed(ori.m_speed),
m_direction(ori.m_direction)
{
ori.m_wheels = array<Wheel, 2>();
ori.m_speed = 0.0;
ori.m_direction = 0.0;
}
(m_wheels is a member of type std::array<Wheel, 2>, and Wheel only contains a double m_speed and a bool m_rotating. In the Motorcycle class, m_speed and m_direction are also doubles.)
I don’t quite understand why ori’s values need to be cleared.
If a Motorcycle had any pointer members we wanted to “steal”, then sure, we’d have to set ori.m_thingy = nullptr so as to not, for example, delete m_thingy twice. But does it matter when the fields contain the objects themselves?
I asked a friend about this, and they pointed me towards this page, which says:
Move constructors typically "steal" the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors, TCP sockets, I/O streams, running threads, etc), rather than make copies of them, and leave the argument in some valid but otherwise indeterminate state. For example, moving from a std::string or from a std::vector may result in the argument being left empty. However, this behaviour should not be relied upon.
Who defines what indeterminate state means? I don’t see how setting the speed to 0.0 is any more indeterminate than leaving it be. And like the final sentence in the quote says — code shouldn’t rely on the state of the moved-from Motorcycle anyway, so why bother cleaning it up?
They don't need to be cleaned. The designer of the class decided it would be a good idea to leave the moved-from object zero initialized.
Note that a situation where is does make sense is for objects managing resources that get released in the destructor. For instance, a pointer to dynamically allocated memory. Leaving the pointer in the moved-from object unmodified would mean two objects managing the same resource. Both their destructors would attempt to release.
Who defines what indeterminate state means?
The author of the class.
If you look at the documentation for std::unique_ptr, you'll see that after the following lines:
std::unique_ptr<int> pi = std::make_unique<int>(5);
auto pi2 = std::move(pi);
pi is actually in a very defined state. It will have been reset() and pi.get() will be equal to nullptr.
You're very probably correct that the author of this class is putting unnecessary operations in the constructor.
Even if m_wheels were a heap-allocated type, such as std::vector, there would still be no reason to "clear" it, since it is already being passed to its own move-constructor:
: m_wheels(std::move(ori.m_wheels)), // invokes move-constructor of appropriate type
However, you have not shown enough of the class to permit us to know whether the explicit "clearing" operations are necessary in this particular case. As per Deduplicator's answer, the following functions are expected to behave correctly in the "moved-from" state:
// Destruction
~Motorcycle(); // This is the REALLY important one, of course
// Assignment
operator=(const Motorcycle&);
operator=(Motorcycle&&);
Therefore, you must look at the implementation of each of these functions in order to determine whether the move-constructor is correct.
If all three use the default implementation (which, from what you've shown, seems reasonable), then there's no reason to manually clear the moved-from objects. However, if any of these functions use the values of m_wheels, m_speed, or m_direction to determine how to behave, then the move-constructor may need to clear them in order to ensure the correct behavior.
Such a class design would be inelegant and error-prone, since typically we would not expect the values of primitives to matter in the "moved-from" state, unless the primitives are explicitly used as flags to indicate whether clean-up must occur in the destructor.
Ultimately, as an example for use in a C++ class, the move-constructor shown is not technically "wrong" in the sense of causing undesired behavior, but it seems like a poor example because it is likely to cause exactly the confusion that led you to post this question.
There are few things which must be safely doable with a moved-from object:
Destruct it.
Assign / Move to it.
Any other operations which explicitly say so.
So, you should move from them as fast as possible while giving those few guarantees, and more only if they are useful and you can fulfill them for free.
Which often means not clearing the source, and other times implies doing it.
As an addition, prefer explicitly defaulted over user-defined functions, and implicitly defined ones over both, for brevity and preserving triviality.
There is no standard behavior for this. Like with pointer you can still use them after you deleted them. Some people see you should not care and just not reuse the object but the compiler won't prohibit that.
Here is one blogpost (not by me) about this with some interesting discussion in the comment:
https://foonathan.github.io/blog/2016/07/23/move-safety.html
and the follow up:
https://foonathan.github.io/blog/2016/08/24/move-default-ctor.html
And here with also a recent Video chat about this topic with arguments discussing this:
https://www.youtube.com/watch?v=g5RnXRGar1w
Basically it's about treating a moved-from object like a deleted pointer or making it in a safe to be "moved-from-state"
Who defines what indeterminate state means?
The English language, I think. "Indeterminate" here has one of its usual English meanings, "Not determinate; not precisely fixed in extent; indefinite; uncertain. Not established. Not settled or decided". The state of a moved-from object is not constrained by the standard other than that it must be "valid" for that object type. It need not be the same every time an object is moved from.
If we were only talking about types provided by the implementation, then the proper language would be a "valid but otherwise unspecified state". But we reserve the word "unspecified" for talking about details of the C++ implementation, not details of what user code is allowed to do.
"Valid" is defined separately for each type. Taking integer types as an example, trap representations are not valid and anything that represents a value is valid.
That standard here is not saying that the move constructor must make the object be indeterminate, merely that it needn't put it into any determined state. So although you're correct that 0 is not "more indeterminate" than the old value, it is in any case moot since the move constructor needn't make the old object "as indeterminate as possible".
In this case, the author of the class has chosen to put the old object into one specific state. It's then entirely up to them whether they document what that state is, and if they do then it's entirely up to users of the code whether they rely on it.
I would usually recommend not relying on it, because under certain circumstances code that you write thinking of it semantically being a move, actually does a copy. For example, you put std::move on the right-hand side of an assignment not caring whether the object is const or not because it works either way, and then somebody else comes along and thinks "ah, it's been moved from, it must have been cleared to zeros". Nope. They've overlooked that Motorcycle is cleared when moved from, but const Motorcycle of course is not, no matter what the documentation might suggest to them :-)
If you're going to set a state at all, then it's really a coin toss which state. You could set it to a "clear" state, perhaps matching what the no-args constructor does (if there is one), on the basis that this represents the most neutral value there is. And I suppose on many architectures 0 is the (perhaps joint-)cheapest value to set something to. Alternatively you could set it to some eye-catcher value, in the hope that when someone writes a bug where they accidentally move from an object and then use its value, they'll think to themselves "What? The speed of light? In a residential street? Oh yeah, I remember, that's the value this class sets when moved-from, I probably did that".
The source object is an rvalue, potentially an xvalue. So the thing one needs to be concerned with is the imminent destruction of that object.
Resource handles or pointers are the most significant item that distinguishes a move from a copy: after the operation the ownership of that handle is assumed to be transferred.
So obviously, as you mention in the question, during a move we need to affect the source object so that it no-longer identifies itself as owner of transferred objects.
Thing::Thing(Thing&& rhs)
: unqptr_(move(rhs.unqptr_))
, rawptr_(rhs.rawptr_)
, ownsraw_(rhs.ownsraw_)
{
the.ownsraw_ = false;
}
Note that I don't clear rawptr_.
Here a design decision is made that, as long as the ownership flag is only true for one object, we don't care if there is a dangling pointer.
However, another engineer might decide that the pointer should be cleared so that instead of random ub, the following mistake results in a nullptr access:
void MyClass::f(Thing&& t) {
t_ = move(t);
// ...
cout << t;
}
In the case of something as innocuous as the variables shown in the question, they may not need to be cleared but that depends on the class design.
Consider:
class Motorcycle
{
float speed_ = 0.;
static size_t s_inMotion = 0;
public:
Motorcycle() = default;
Motorcycle(Motorcycle&& rhs);
Motorcycle& operator=(Motorcycle&& rhs);
void setSpeed(float speed)
{
if (speed && !speed_)
s_inMotion++;
else if (!speed && speed_)
s_inMotion--;
speed_ = speed;
}
~Motorcycle()
{
setSpeed(0);
}
};
This is a fairly artificial demonstration that ownership isn't necessarily a simple pointer or bool, but can be an issue of internal consistency.
The move operator could use setSpeed to populate itself, or it could just do
Motorcycle::Motorcycle(Motorcycle&& rhs)
: speed_(rhs.speed_)
{
rhs.speed_ = 0; // without this, s_inMotion gets confused
}
(Apologies for typos or autocorrects, typed on my phone)
In C, we have the functions memcpy and memmove to efficiently copy data around. The former yields undefined behavior if the source and destination regions overlap, but the latter is guaranteed to deal with that "as expected," presumably by noticing the direction of overlap and (if necessary) choosing a different algorithm.
The above functions are available in C++ (as std::memcpy and std::memmove), of course, but they don't really work with non-trivial classes. Instead, we get std::copy and std::copy_backward. Each of these works if the source and destination ranges don't overlap; moreover, each is guaranteed to work for one "direction" of overlap.
What can we use if we want to copy from one region to another and we don't know at compile-time if the ranges may overlap or in what direction that overlap may occur? It doesn't seem that we have an option. For general iterators it may be difficult to determine if ranges overlap, so I understand why no solution is provided in that case, but what about when we're dealing with pointers? Ideally, there'd be a function like:
template<class T>
T * copy_either_direction(const T * inputBegin, const T * inputEnd, T * outputBegin) {
if ("outputBegin ∈ [inputBegin, inputEnd)") {
outputBegin += (inputEnd - inputBegin);
std::copy_backward(inputBegin, inputEnd, outputBegin);
return outputBegin;
} else {
return std::copy(inputBegin, inputEnd, outputBegin);
}
}
(A similar function with T * replaced with std::vector<T>::iterator would also be nice. Even better would be if this were guaranteed to work if inputBegin == outputBegin, but that's a separate gripe of mine.)
Unfortunately, I don't see a sensible way to write the condition in the if statement, as comparing pointers into separate blocks of memory often yields undefined behavior. On the other hand, the implementation clearly has its own way to do this, as std::memmove inherently requires one. Thus, any implementation could provide such a function, thereby filling a need that the programmer simply can't. Since std::memmove was considered useful, why not copy_either_direction? Is there a solution I'm missing?
memmove works because it traffics in pointers to contiguous bytes, so the ranges of the two blocks to be copied are well defined. copy and move take iterators that don't necessarily point at contiguous ranges. For example, a list iterator can jump around in memory; there's no range that the code can look at, and no meaningful notion of overlap.
I recently learned that std::less is specialized for pointers in a way that provides a total order, presumably to allow one to store pointers in std::sets and related classes. Assuming that this must agree with the standard ordering whenever the latter is defined, I think the following will work:
#include <functional>
template<class T>
T * copy_either_direction(const T * inputBegin, const T * inputEnd, T * outputBegin) {
if (std::less<const T *>()(inputBegin, outputBegin)) {
outputBegin += (inputEnd - inputBegin);
std::copy_backward(inputBegin, inputEnd, outputBegin);
return outputBegin;
} else {
return std::copy(inputBegin, inputEnd, outputBegin);
}
}
What can we use if we want to copy from one region to another and we don't know at compile-time if the ranges may overlap or in what direction that overlap may occur?
This is not a logically consistent concept.
After a copy operation, you will have two objects. And each object is defined by a separate and distinct region of memory. You cannot have objects which overlap in this way (you can have subobjects, but an object type cannot be its own subobject). And therefore, it is impossible to copy an object on top of part of itself.
To move an object on top of itself is also not logically consistent. Why? Because moving is a fiction in C++; after the move, you still have two perfectly functional objects. A move operation is merely a destructive copy, one that steals resources owned by the other object. It is still there, and it is still a viable object.
And since an object cannot overlap with another object, it is again impossible.
Trivially copyable types get around this because they are just blocks of bits, with no destructors or specialized copy operations. So their lifetime is not as rigid as that of others. A non-trivially copyable type cannot do this because:
The experience with memmove suggests that there could be a solution in this case (and perhaps also for iterators into contiguous containers).
This is neither possible nor generally desirable for types which are not trivially copyable in C++.
The rules for trivial copyability are that the type has no non-trivial copy/move constructors/assignment operators, as well as no non-trivial destructor. A trivial copy/move constructor/assignment is nothing more than a memcpy, and a trivial destructor does nothing. And therefore, these rules effectively ensures that the type is nothing more than a "block of bits". And one "block of bits" is no different from another, so copying it via memmove is a legal construct.
If a type has a real destructor, then the type is maintaining some sort of invariant that requires actual effort to maintain. It may free up a pointer or release a file handle or whatever. Given that, it makes no sense to copy the bits, because now you have two objects that reference the same pointer/file handle. That's a bad thing to do, because the class will generally want to control how that gets handled.
There is no way to solve this problem without the class itself being involved in the copy operation. Different class have different behavior with respect to managing their internals. Indeed, that is the entire purpose of objects having copy constructors and assignment operators. So that a class can decide for itself how to maintain the sanity of its own state.
And it doesn't even have to be a pointer or file handle. Maybe each class instance has a unique identifier; such a value is generated at construction time, and it never gets copied (new copies get new values). For you to violate such a restriction with memmove leaves your program in an indeterminate state, because you will have code that expects such identifiers to be unique.
That's why memmoveing for non-trivially copyable types yields undefined behavior.
I am aware that you have to be careful with auto pointers (and why), especially with the STL. But I don't see a problem with this:
std::map<T1, std::auto_ptr<T2> >;
Is this safe?
I see how it would break in an std::vector, because it has to copy its items from time to time, but is this also true for the value type of an std::map?
Edit: Apparently, regardless whether it's safe, I cannot (technically) populate the map, but I'll leave the question open for theoretical considerations. Otherwise, consider it closed.
It's not safe. Technically, std::auto_ptr doesn't meet the requirement of CopyConstructible or Assignable because copies made using auto_ptr's copy constructor or copy assignment operator aren't equivalent to the source of the copy after the copy operation. These requirements must be met for any type used with any standard container.
You may find that you appear to get the behaviour you expect on one implementation for one particular use case but if you violate the requirements of the container you can't expect your code to work in all situations.
Still not safe. If nothing else, the first time you retrieved the pointer from the map you would transfer ownership and make it invalid.
There is an extremely narrow valid use case for auto_ptr. The only one I can remember coming across is when an object wants to make it explicitly clear that it takes ownership and responsibility for a pointer that you hand it.
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.