std :: vector <T>:: назначить с использованием допустимого поддиапазона? - PullRequest
9 голосов
/ 26 сентября 2019

Я хочу преобразовать вектор в поддиапазон этого вектора, например, удалив первое и последнее значения.Является ли использование функции-члена назначения допустимым в этом контексте?

std::vector<int> data = {1, 2, 3, 4};
data.assign(data.begin() + 1, data.end() - 1);
// data is hopefully {2, 3}

Cppreference утверждает, что

Все итераторы, указатели и ссылки на элементыконтейнер признан недействительным.Итератор конца-в-конце также становится недействительным.

Однако эта аннулирование, по-видимому, не происходит до конца присвоения .

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

std::vector<int> data = {1, 2, 3, 4};
data = std::vector<int>{data.begin() + 1, data.end() - 1};
// data is now {2, 3}

1 Ответ

10 голосов
/ 26 сентября 2019

Функция __invalidate_all_iterators, на которую ссылается ваша ссылка, является просто средством отладки.Это не «делает» итераторы недействительными;Это эффективно сообщает, что итераторы были признаны недействительными предыдущими действиями.Может случиться так, что этот инструмент отладки может не обнаружить ошибку, вызванную этим назначением.

Предварительным условием assign является то, что итераторы не относятся к одному и тому же контейнеру.Нарушение предусловия приводит к неопределенному поведению.

Стандартная цитата (последний черновик):

[sequence.reqmts] a.assign(i,j) Ожидается: T является Cpp17EmplaceConstructible в X из * i и может быть назначеноот * я.Для вектора, если итератор не удовлетворяет требованиям прямого итератора ([forward.iterators]), T также Cpp17MoveInsertable в X. Ни i, ни j не являются итераторами в.

Ваша безопасная альтернатива верна.

Если вы хотите избежать перераспределения (имея в виду, что останется неиспользованное пространство), и если вы хотите избежать копирования (что важно для сложных типов, не 'Если значение имеет int), то должно быть эффективно следующее:

int begin_offset = 1;
int end_offset = 1;
if (begin_offset)
    std::move(data.begin() + begin_offset, data.end() - end_offset, data.begin());
data.erase(data.end() - end_offset - begin_offset, data.end());
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...