无效迭代器的算术

Arithmetic on invalidated iterators

在一对 std::vector 迭代器上调用时 std::distance 的行为是否未定义,这些迭代器已通过移动 vector 失效?

上下文: 我正在为 class 编写复制和移动构造函数,它具有 vector 数据和 vector 指向该数据的迭代器。将数据移动到目的地后,我需要转换迭代器向量以指向新容器。我想避免在内存中创建中间索引表示。

Is behavior of std::distance undefined when called on a pair of std::vector iterators that have been invalidated by moving the vector?

如果迭代器在移动前有效,它们在移动后将保持有效 - 因此您无需使用 std::distance.

重新计算它们

(在下面强调我的)

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] 表示

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.

同样的一揽子声明适用于移动赋值运算符,这意味着,根据标准,迭代器在移动后保持有效。

LWG 2321 中的当前措辞暗示了如果图书馆工作组最终确定标准中的新段落可能会是什么样子——这似乎很难。 LWG 2321 于 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]

如果太模糊,您可以使用

[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 ]

如果迭代器在之前swap是有效的,它们在之后swap是有效的。

这里有一个例子 class 使用为 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;
};