Невалидни векторни итератори

std::vector итераторите могат да бъдат реализирани като указатели. Следствието е, че ако добавите елементи към вектора, неизпълнените итератори очевидно ще станат невалидни, защото като цяло векторните данни ще трябва да бъдат преразпределени.

Първото предположение относно точните правила би било, че разрешените операции са точно същите като тези за указатели, напр. не дереферирайте невалиден итератор, докато не му бъде преназначена валидна стойност, но това не изглежда съвсем вярно, защото внедряването на Microsoft в режим на отстраняване на грешки понякога ще хвърли изключение, ако напр. извадете векторни итератори, сочещи към различни блокове данни (което е полезно за отстраняване на грешки, разбира се).

Дали добавката към правилата за указателя е нещо като „не изваждайте итератори към различни блокове данни“ или „не правете никаква аритметика на невалиден итератор, докато не му бъде преназначена валидна стойност“ или нещо друго?

Например, валидна ли е следната програма (която изглежда работи както на Microsoft C++, така и на GCC)?

#include <algorithm>
#include <iostream>
#include <vector>

using std::cout;
using std::ostream;
using std::vector;

template<class T> ostream& operator<<(ostream& os, vector<T>& v) {
    os << '[';
    bool c = 0;
    for (auto a: v) {
        if (c)
            os << ", ";
        c = 1;
        os << a;
    }
    return os << ']';
}

void f(vector<int>& v, vector<int>::iterator& i) {
    *i = 10;
    for (int j = 0; j < 10; j++)
        v.insert(begin(v), j);
    i = begin(v)+5;
}

int main() {
    vector<int> v;
    for (int i = 0; i < 10; i++)
        v.push_back(i);
    auto i = begin(v)+5;
    f(v, i);
    i[1] = 11;
    cout << v << '\n';
    return 0;
}

person rwallace    schedule 27.06.2013    source източник
comment
Какво точно имате предвид под итератори, сочещи към различни блокове данни?   -  person jogojapan    schedule 27.06.2013
comment
И защо примерната програма няма да е валидна? В кой момент се случва нещо, което смятате, че не е валидно?   -  person jogojapan    schedule 27.06.2013
comment
@jogojapan различни вектори или елементи от един и същи вектор преди и след добавянето на нови елементи е причинило преразпределение.   -  person rwallace    schedule 27.06.2013
comment
Той вмъква множество елементи, ако вмъкне достатъчно елементи, тогава ще трябва да преразпределите вектора (възможно в друго място в паметта) и тогава „i“ може да стане невалиден   -  person Tomer Arazy    schedule 27.06.2013
comment
@jogojapan мисля, че е валидно; прави невалиден итератор, но не прави нищо с него, освен да му присвои отново валидна стойност. Просто се опитвам да разбера къде точно е границата, от която се надявам да съм от правилната страна.   -  person rwallace    schedule 27.06.2013
comment
@rwallace Добре. Изваждането (и след това дереферирането) на итератори от различни вектори е невалидно (но това е същото като за указателите). И изваждането (и след това дереферирането) на итератори от преди и след повторно разпределение е невалидно две (същото както когато преразпределяте конвенционален масив).   -  person jogojapan    schedule 27.06.2013
comment
@TomerArazy Но i се нулира след вмъкването, като се използва begin(v). Това е напълно валидно.   -  person jogojapan    schedule 27.06.2013
comment
@jogojapan правилно, но с указатели простото действие на изваждане е добре, ако след това не дереферирате, нали? Или стандартът C++ сега счита това за строго казано недефинирано поведение дори за указатели?   -  person rwallace    schedule 27.06.2013
comment
@jogojapan - Прав си, пропуснах това   -  person Tomer Arazy    schedule 27.06.2013
comment
Точните правила за невалидност на итератора са посочени в стандарта и не е необходимо да се предполага. Съгласно тези правила поведението на вашата програма е недефинирано.   -  person n. 1.8e9-where's-my-share m.    schedule 27.06.2013
comment
@rwallace Хммм. Добре. Строго погледнато, наистина, операцията за изваждане от итератор с произволен достъп на сума, по-голяма от неговото разстояние от началото, не е дефинирана. Тоест самата операция не е дефинирана. Може би тук има разлика с указателите. (Също така имам чувството, че тук се разцепваме... за всички намерения и цели итераторите са предназначени да действат по същия начин като (или като абстракция на) указатели, §24.2.1).   -  person jogojapan    schedule 27.06.2013
comment
с указатели самото действие на изваждане е добре, ако след това не дереферирате ― не, не е. Всяка аритметична операция с указател е UB, ако операндът или резултатът са невалидни.   -  person n. 1.8e9-where's-my-share m.    schedule 27.06.2013
comment
@n.m. интересно Не бях сигурен за това спрямо указателите. Но относно предишния ви коментар: Каква част от програмата е UB?   -  person jogojapan    schedule 27.06.2013
comment
Моя грешка, вероятно не е недефиниран, просто има твърде много променливи с име i.   -  person n. 1.8e9-where's-my-share m.    schedule 27.06.2013


Отговори (1)


Вашият пример не е валиден, причината да работи е късмет. Всяка операция, която евентуално може да причини преразпределение във вектор, може да обезсили всички итератори.

person Marius    schedule 27.06.2013
comment
Бихте ли обяснили за коя част от кода се отнасяте конкретно? Също така имайте предвид, че самото обезсилване на итератор не означава, че програмата е невалидна. Това просто означава, че има определени неща, които не трябва да правите с този итератор. - person jogojapan; 27.06.2013