Изменение содержимого вектора в BOOST_FOREACH
Вопрос
Этот вопрос касается того, как BOOST_FOREACH проверяет завершение цикла
cout << "Testing BOOST_FOREACH" << endl;
vector<int> numbers; numbers.reserve(8);
numbers.push_back(1); numbers.push_back(2); numbers.push_back(3);
cout << "capacity = " << numbers.capacity() << endl;
BOOST_FOREACH(int elem, numbers)
{
cout << elem << endl;
if (elem == 2) numbers.push_back(4);
}
cout << "capacity = " << numbers.capacity() << endl;
дает вывод
Testing BOOST_FOREACH
capacity = 8
1
2
3
capacity = 8
Но как насчет числа 4, которое было вставлено в середине цикла? Если я изменю тип на список, вновь вставленный номер будет повторен. Операция векторного push_back сделает недействительными любые указатели, ЕСЛИ требуется перераспределение, однако в этом примере этого не происходит. Поэтому вопрос, который я предполагаю, заключается в том, почему итератор end () вычисляется только один раз (перед циклом) при использовании вектора, но имеет более динамическую оценку при использовании списка?
Решение
Под обложками BOOST_FOREACH использует итераторы для прохождения элемента последовательность. Перед выполнением цикла конечный итератор кэшируется в локальном переменная. Это называется подъем, и это важная оптимизация. Это предполагает, однако, что конец Итератор последовательности стабилен. Это как правило, но если мы изменим последовательность путем добавления или удаления элементы в то время как мы перебираем это, мы можем в конечном итоге поднять себя на нашей собственной петарде.
http://www.boost.org/ DOC / LIBS / 1_40_0 / DOC / HTML / Еогеасп / pitfalls.html р>
Если вы не хотите, чтобы итератор end () изменял, используйте resize для вектора, а не для резерва.
http://www.cplusplus.com/reference/stl/vector/resize / р>
Обратите внимание, что тогда вы не захотите push_back, а вместо этого будете использовать оператор []. Но будьте осторожны, выходя за пределы.
Другие советы
В комментариях был поднят вопрос, почему среда выполнения отладки Microsoft поднимает утверждение во время итерации по вектору, но не по списку. Причина в том, что insert
определяется по-разному для list
и vector
(обратите внимание, что push_back
является просто вставить
в конец последовательности).
В соответствии со стандартом C ++ (ISO / IEC 14882: 2003 23.2.4.3, векторные модификаторы ):
[при вставке], если перераспределение не происходит, все итераторы и ссылки до точки вставки остаются действительными.
(23.2.2.3, модификаторы списка ):
[insert] не влияет на достоверность итераторов и ссылок.
Итак, если вы используете push_back
(и уверены, что это не приведет к перераспределению), то в любом контейнере можно продолжить использование итератора для итерации по остальной части последовательности. р>
Однако в случае вектора неопределенное поведение использовать итератор end
, который вы получили до push_back
. р>
Это окольный ответ на вопрос; это прямой ответ на обсуждение в комментариях к вопросу.
foreach boost прекратит работу, когда будет выполнен итератор == numbers.end ()
Будьте осторожны, вызов push_back может / сделает недействительными все ваши текущие итераторы.