简体   繁体   中英

Arithmetic on invalidated iterators

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

For context: I'm writing copy and move constructors for a class that has a vector of data and a vector of iterators that point to that data. Once I move the data to its destination, I need to translate the vector of iterators to point to the new container. I would like to avoid creating intermediate index representation in memory.

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;
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM