Добавление вектора std::с его собственными элементами с помощью итераторов


Следующий код работает как ожидалось (тест проходит), но мне интересно, считается ли работа с итераторами таким образом плохой практикой в c++ или это нормально.

Может быть, это специфично для std::vector , а другие коллекции ведут себя по-разному, и рекомендации различаются между коллекциями (или даже их реализациями)?

Это, конечно, не в порядке в других языках, и большую часть времени изменение коллекции сделает недействительными итераторы и бросит исключения.

BOOST_AUTO_TEST_CASE (ReverseIteratorExample) {
    std::vector<int> myvector;
    for(int i = 0; i < 5; i++)
    {
        myvector.push_back(i);
    }

    // is this generally a bad idea to change the vector while iterating?
    // is it okay in this specific case?
    myvector.reserve(myvector.size() + myvector.size() - 2 );
    myvector.insert(myvector.end(), myvector.rbegin() + 1, myvector.rend() -1);

    int resultset [8] = { 0,1,2,3,4,3,2,1 };
    std::vector<int> resultVector( resultset, resultset + sizeof(resultset)/sizeof(resultset[0]) );
    BOOST_CHECK_EQUAL_COLLECTIONS(myvector.begin(), myvector.end(), resultVector.begin(), resultVector.end());
}

Обобщенные Вопросы:

  1. это вообще плохая идея, чтобы изменить вектор во время итерации?
  2. это нормально в данном конкретном случае?
  3. является ли это специфичным для std::vector и других коллекций, которые ведут себя по-разному?
  4. различаются ли лучшие практики между коллекциями (или даже их реализациями)?
1 6

1 ответ:

Это недопустимый код. Стандартное определение операций над состояниями контейнеров последовательностей (23.2.3@4):

A. вставить(p, i, j) - [...] pre: i и j не являются итераторами в a.

Таким образом, ваш код вызывает неопределенное поведение, потому что он нарушает предварительное условие для операции insert.

Если вместо использования insert, вы написали цикл итерации от myvector.rbegin() + 1 до myvector.rend() -1 и вызвали push_back по всем значениям, ваш код будет действителен: это потому, что push_back только делает недействительными векторные итераторы, если требуется перераспределение, и ваш вызов reserve гарантирует, что это не так.

В целом, хотя есть некоторые случаи, когда изменение контейнера во время итерации над ним нормально (например, цикл, описанный выше), вы должны убедиться, что ваши итераторы не являются недействительными при этом. Когда это происходит, зависит от каждого контейнера.