Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is the capacity required to be preserved when moving a std::vector?

Consider the following code:

std::vector vec;
vec.reserve(500);
size_t cap = vec.capacity();

std::vector newVec = std::move(vec);
assert(cap == newVec.capacity());

In pretty much any implementation you run across, this will work. I don't care about what implementations do. I want to know what the standard requires. Will the moved-to vector have the same capacity as the original? Or will the assert trigger?

like image 217
Nicol Bolas Avatar asked Oct 29 '12 20:10

Nicol Bolas


People also ask

What happens when you std :: move a vector?

std::move. std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object. In particular, std::move produces an xvalue expression that identifies its argument t . It is exactly equivalent to a static_cast to an rvalue reference type.

Does vector clear change capacity?

The standard mandates that the capacity does not change with a clear as reserve guarantees that further adding of elements do not relocate until the requested capacity is reached. This directly enforces clear to not reduce the capacity.

Is std::vector movable?

std::vector typically stores some pointers plus the allocator. When move-constructing a std::vector , only pointers and the allocator have to be moved. The elements don't need to be touched.

How do you reserve the size of a vector?

C++ Vector Library - reserve() FunctionThe C++ function std::vector::reserve() requests to reserve vector capacity be at least enough to contain n elements. Reallocation happens if there is need of more space.


2 Answers

Looking at the standard, it appears that nothing is required from the move constructor, however as @amaurea says, it would completely defeat the purpose of move semantics if the move constructor were to try and allocate or deallocate memory, so I would expect the capacity to remain the same in all implementations.


23.2.1 General container requirements

Expression

X u(a);
X u = a;

Assertion/note pre-/post-condition

Requires: T is CopyInsertable into X (see below).
post: u == a


The standard only requires that newVec == vec. As capacity is not taken into consideration for std::vector::operator==, newVec need not necessarily have the same capacity as vec.

like image 83
Peter Alexander Avatar answered Oct 18 '22 02:10

Peter Alexander


C++11 standard requirements on move constructor for std::vector are (Table 99 — Allocator-aware container requirements):

X(rv)
X u(rv)
  • move construction of allocator shall not exit via an exception
  • post: u shall have the same elements as rv had before this construction; the value of get_allocator() shall be the same as the value of rv.get_allocator() before this construction.
  • complexity: constant

Here is no requirements/guarantee on capacity. But we can make conclusion that constant complexity implicitly denies any reallocations. And I cannot see any other logical reason to change capacity except reallocation. So it shall be the same.

From the other point of view if the moved-from vector is empty, it is perfectly legal to just ignore it and default-construct itself. This would still be O(1), as it doesn't require any per-element constructs. (Thanks to Nicol Bolas for this issue).

Also implemenation possibly could shrink capacity to the size using hint parameter of std::allocator::allocate function:

pointer allocate(size_type, allocator<void>::const_pointer hint = 0);

The use of hint is unspecified, but intended as an aid to locality if an implementation so desires. So some sofisticated solution possibly could pass vector storage pointer as hint and use realloc on it to shrink capacity.

Conclusion: looks like standard doesn't guarantee capacity preserving on moving std::vector, storage potentially could be shrinked.

like image 40
Rost Avatar answered Oct 18 '22 00:10

Rost