Is behavior of std::distance
undefined when called on a pair of std::vector
iterators that have been invalidated by moving the vector
?
If the iterators are valid before the move, they will remain valid after the move - so you don't need to recalculate them using std::distance
.
(emphasis mine below)
std::vector::vector
After container move construction, references, pointers, and iterators (other than the end iterator) to other
remain valid, but refer to elements that are now in *this
.
The current standard makes this guarantee via the blanket statement in [container.requirements.general/12], and a more direct guarantee is under consideration via LWG 2321.
[container.requirements.general/12] states that
Unless otherwise specified (either explicitly or by defining a function in terms of other functions), invoking a container member function or passing a container as an argument to a library function shall not invalidate iterators to, or change the values of, objects within that container.
The same blanket statement goes for the move assignment operator and this means that, in accordance with the standard, the iterators will stay valid after the move.
The current wording in LWG 2321 gives a hint of what a new paragraph in the standard could look like if the library working group finalize this - which seems to be hard. LWG 2321 was opened back in 2013.
no move constructor (or move assignment operator when allocator_traits<allocator_type>::propagate_on_container_move_assignment::value
is true
) of a container (except for array
) invalidates any references, pointers, or iterators referring to the elements of the source container. [Note: The end()
iterator does not refer to any element, so it may be invalidated. — end note]
If that's too vague, you can use
[container.requirements.general/11.6]
no swap()
function invalidates any references, pointers, or iterators referring to the elements of the containers being swapped. [ Note: The end()
iterator does not refer to any element, so it may be invalidated. — end note ]
If the iterators are valid before you swap
, they are valid after the swap
.
Here's an example class using the guarantee given for swap
:
#include <vector>
class Foo {
std::vector<int> data{};
std::vector<decltype(data)::iterator> dits{};
public:
Foo() = default;
Foo(const Foo&) = delete; // here, dits would need to be calculated
// A move constructor guaranteed to preserve iterator validity.
Foo(Foo&& rhs) noexcept {
data.swap(rhs.data);
dits.swap(rhs.dits);
}
Foo& operator=(const Foo&) = delete;
// A move assignment operator guaranteed to preserve iterator validity.
Foo& operator=(Foo&& rhs) noexcept {
data.swap(rhs.data);
dits.swap(rhs.dits);
return *this;
}
~Foo() = default;
};