Делает ли std :: vector :: swap недействительными итераторы? - PullRequest
39 голосов
/ 08 ноября 2010

Если я поменяю два вектора, останутся ли их итераторы действительными, теперь просто указывая на «другой» контейнер, или итератор будет признан недействительным?

То есть, учитывая:

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?

Кажется, в стандарте ничего не говорится об этом:

[n3092 - 23.3.6.2]

void swap(vector<T,Allocator>& x);

Эффекты: обмен содержимым и емкостью () этого с x.

Обратите внимание, что, поскольку я нахожусь на VS 2005, меня также интересуют эффекты отладочных проверок и т. д. (_SECURE_SCL)

Ответы [ 4 ]

29 голосов
/ 08 ноября 2010

Поведение подкачки было значительно разъяснено в C ++ 11, в значительной степени, чтобы позволить алгоритмам стандартной библиотеки использовать аргумент-зависимый поиск (ADL) для поиска функций подкачки для пользовательских типов.C ++ 11 добавляет концепцию swappable (C ++ 11 §17.6.3.2 [swappable.requirements]), чтобы сделать это законным (и обязательным).

Текст в C +Языковым стандартом +11, который отвечает на ваш вопрос, является следующий текст из требований к контейнерам (§23.2.1 [container.requirements.general] / 8), который определяет поведение функции-члена swap контейнера:

Каждый итератор, ссылающийся на элемент в одном контейнере до свопа, должен ссылаться на тот же элемент в другом контейнере после свопа.

Не определено, является ли итератор со значением a.end() переду свопа будет значение b.end() после свопа.

В вашем примере a гарантированно будет действительным после свопа, но b не потому, что это конечный итератор.Причина, по которой конечные итераторы не гарантируются как действительные, объясняется в примечании к §23.2.1 / 10:

[Примечание: итератор end() не ссылается ни на один элемент, поэтому можетбыть признанным недействительным- end note]

Это то же самое поведение, которое определено в C ++ 03, только существенно разъяснено.Исходный язык C ++ 03 находится на C ++ 03 §23.1 / 10:

no swap() Функция делает недействительными любые ссылки, указатели или итераторы, ссылающиеся на элементы переставляемых контейнеров..

Это не сразу очевидно в исходном тексте, но фраза "элементам контейнеров" чрезвычайно важна, поскольку итераторы end() не указывают на элементы.

13 голосов
/ 08 ноября 2010

Замена двух векторов не делает недействительными итераторы, указатели и ссылки на его элементы (C ++ 03, 23.1.11).

Обычно итератор содержит сведения о своем контейнере и операции обменаподдерживает это для данного итератора.

В VC ++ 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;
};
1 голос
/ 08 ноября 2010

Что касается 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 */
 ...

Итераторы указывают на свои оригинальные элементы в теперь замененном векторном объекте.(Т. Е. К OP, они сначала указали на элементы в x, после свопа они указывают на элементы в y.)

Обратите внимание, что в черновике n3092 требование изложено в§23.2.1 / 9:

Каждый итератор, ссылающийся на элемент в одном контейнере до обмена, должен ссылаться на тот же элемент в другом контейнере после обмена.

...