In std::vector
there is a member function called resize
.
The utility of resize
is two fold, first, it preserves elements the existing elements when it makes sense, and, second, it avoids allocations if they are not necessary. (in contrast to simply doing v = std::vector<T>(new_size);
which would do neither.)
There is only one version of resize, one that takes the object by l-value reference (v.resize(new_size)
).
https://en.cppreference.com/w/cpp/container/vector/resize
Supposed I am writing a container similar to std::vector
and I want to implement a version of resize
that is r-value aware.
I would say that, since the original object is in a "moved" state, element preservation is not necessary.
Which in principle can produce a more efficient resize because element preservation is not necessary (this is particularly interesting in the case that the resize surpasses the current capacity.)
I think the operation is valid
But I am not sure what is a consistent final state.
my_vector<double> v(3, 42.);
std::move(v).resize(6);
What should be the state of v
? What static is more consistent with the current philosophy of moved objects?
Here some options:
v
has 6 elements and each element is totally unspecified.v
is{42., 42., 42., 0., 0., 0.}
v
is{unspecified, unspecified, unspecified, 0., 0., 0.}
v
is{0., 0., 0., 0., 0., 0.}
Case 2 is what the current non r-value-aware vector::resize
would do.
I have a slight inclination towards case 3.
Implementation wise it is possible that 4 is the "likely" (i.e. undocumented) result (as an instance of the unspecified behavior of case 3). Since case 2 could also be a consistent instance, I seem that case 3 or 1 are the only formal options left.
Take into account that it is possible that resize to a larger value (6) requires reallocation.
I am using double
for illustration as a place holder for a non-trivial type that might be expensive to copy.
And 0.
is not special except for being the default constructed state (T()
).
The question is equally valid with this slightly different case:
std::move(v).resize(6, 99.);
1. `v` has 6 elements and each element is totally unspecified.
2. `v` is `{42., 42., 42., 99., 99., 99.}`
3. `v` is `{unspecified, unspecified, unspecified, 99., 99., 99.}`
4. `v` is `{99., 99., 99., 99., 99., 99.}`
(Note that in any case std::move(v).resize(6);
has the chance to do at least slight better than v = my_vector<T>(6)
because the latter always allocate and always initializes all the elements.)
A flaw in the reasoning is that
std::move(x)
in itself doesn't move anything. It could have been namedenable_move
orallow_move
, if those names hadn't been too long. It marks things as "movable", but doesn't perform the move operation. So, as long asresize
doesn't have an rvalue overload, nothing is moved by usingstd::move
.However,
std::vector
is already rvalue aware and will (likely) use the helper function move_if_noexcept() to move objects that have a noexcept move constructor.