Reputation: 3991
Assuming I have a container, for the sake of argument let's say a vector:
std::vector<T> v;
and assuming my type 'T' has a move ctor:
T(T&& move_from) noexcept //...implementation
However I have no move assignment operator.
Is it possible to move a value to type T into a location at a certain index inside the vector ? Similar to the way I can insert the element in between two elements or at the end by moving it using:
v.emplace(std::move(value));
v.emplace_back(std::move(value));
... The one way everyone seems to suggest replacing a vector's element is by using:
v.at(index) = value;
However what is bound to invoke the copy ctor (or however the opearator= copy is canonically called).
It has come to my mind to use std::swap
on the element at the desired index and the value I wish to move in, however, it seems to be a bit of a waste since I don't need the value I am replacing anymore AND if I don't have a swap()
defined on my class the std::swap seems to just invoke the copy ctor of both elements... which just makes the whole thing twice as expensive.
How should one go about moving an element at an index in a vector and in an stl container in general (if this can be generalized) ? Is there a reason why this shouldn't be doable in the first place ?
Note, I am asking how this is doable on types that have only a standard move ctor defined, I can see it becoming fairly easy if I define a swap() on the types. However, the existence of the swap method doesn't seem to be as common so I'd prefer to avoid that... not to mention that when the method is not available, I'd have to either figure that out with sfinae (which will make code unreadable) or suffer the penalty of a double call to the copy ctor.
Upvotes: 0
Views: 554
Reputation: 141554
v.at(index) = std::move(value)
depends on there being a move-assignment operator. The std::swap
solution needs a nothrow move-assignment operator.
Also there is definitely nothing bad about defining T::swap
, it may be marginally more efficient than falling back to std::swap
.
Writing the move-assignment operator for T
would be a good solution. If you can't do that then it's possible to create and destroy in-place, e.g.:
T *ptr = &v.at(index);
ptr->~T();
new(ptr) T( std::move(value) );
which relies on T
's move-constructor being noexcept, otherwise you are stuffed if it throws.
A good reason to use swap
or move-assignment is that it is inherently exception-safe. The move-assignment operator will be beneficial for other container operations too.
Upvotes: 4