Использует ли std::vector:: swap аннулировать итераторы?
Если я поменяю два вектора, будут ли их итераторы оставаться в силе, теперь просто указывая на "другой" контейнер, или итератор будет недействителен?
То есть, данный:
using namespace std;
vector<int> x(42, 42);
vector<int> y;
vector<int>::iterator a = x.begin();
vector<int>::iterator b = x.end();
x.swap(y);
// a and b still valid? Pointing to x or y?
Кажется, std ничего не говорит об этом:
[n3092 - 23.3.6.2]
void swap(vector<T,Allocator>& x);
Эффекты: Обменивает содержимое и емкость() * этого с тем из x.
Обратите внимание, что поскольку я на VS 2005, я также заинтересован в эффектах проверки отладки итератора и т.д. (_SECURE_SCL)
Ответы
Ответ 1
Поведение swap значительно разъяснено в С++ 11, в значительной степени, чтобы позволить алгоритмам стандартной библиотеки использовать зависящий от зависимостей поиск (ADL) для поиска функций подкачки для пользовательских типов. С++ 11 добавляет концепцию swappable (С++ 11 §17.6.3.2 [swappable.requirements]), чтобы сделать это законным (и обязательным).
Текст в стандартном языке С++ 11, который отвечает на ваш вопрос, - это следующий текст из требований контейнера (§23.2.1 [container.requirements.general]/8), который определяет поведение swap
член-функция контейнера:
Каждый итератор, ссылающийся на элемент в одном контейнере перед свопом, должен ссылаться на тот же элемент в другом контейнере после обмена.
Не указано, будет ли итератор со значением a.end()
до того, как swap будет иметь значение b.end()
после свопа.
В вашем примере гарантируется, что a
будет действительным после обмена, но b
не потому, что это итератор конца. Причина, по которой итераторы заканчиваются, не гарантируется, что они действительны, объясняется в примечании в §23.2.1/10:
[Примечание: итератор end()
не ссылается ни на какой элемент, поэтому может быть аннулированной. - примечание]
Это то же поведение, которое определено в С++ 03, только что прояснено. Исходный язык от С++ 03 находится на С++ 03 §23.1/10:
Функция no swap()
делает недействительными любые ссылки, указатели или итераторы, ссылающиеся на элементы обменяемых контейнеров.
Это не сразу видно в исходном тексте, но фраза "элементам контейнеров" чрезвычайно важна, потому что итераторы end()
не указывают на элементы.
Ответ 2
Обмен двумя векторами не отменяет итераторов, указателей и ссылок на его элементы (С++ 03, 23.1.11).
Обычно итератор будет содержать информацию о своем контейнере, а операция подкачки поддерживает это для данного итератора.
В VС++ 10 векторный контейнер управляется с использованием этой структуры в <xutility>
, например:
struct _Container_proxy
{ // store head of iterator chain and back pointer
_Container_proxy()
: _Mycont(0), _Myfirstiter(0)
{ // construct from pointers
}
const _Container_base12 *_Mycont;
_Iterator_base12 *_Myfirstiter;
};
Ответ 3
Все итераторы, которые ссылаются на элементы контейнеров, остаются в силе
Ответ 4
Что касается Visual Studio 2005, я только что протестировал его.
Я думаю, что он всегда должен работать, так как функция vector:: swap даже содержит явный шаг по замене всего:
// vector-header
void swap(_Myt& _Right)
{ // exchange contents with _Right
if (this->_Alval == _Right._Alval)
{ // same allocator, swap control information
#if _HAS_ITERATOR_DEBUGGING
this->_Swap_all(_Right);
#endif /* _HAS_ITERATOR_DEBUGGING */
...
Итераторы указывают на свои исходные элементы в векторном объекте с заменой теперь. (I.e.w/rg к OP, они сначала указывали на элементы в x
, после свопинга они указывают на элементы в y
.)
Обратите внимание, что в проекте n3092 требование изложено в п. 23.2.1/9:
Каждый итератор, ссылающийся на элемент в одном контейнере до подкачка относится к одному и тому же элементу в другом контейнере после обмена.