Обрабатывает ли std::copy перекрывающиеся диапазоны?
При копировании данных из одного диапазона в другой, вы должны быть осторожны, если есть частичное совпадение между исходным и целевым диапазонами. Если начало целевого диапазона перекрывает хвост исходного диапазона, обычная последовательная копия искажает данные. Библиотека времени выполнения C имеет memmove
в дополнение к memcpy
чтобы справиться с такими проблемами перекрытия.
Я предполагаю std::copy
работает как memcpy
в том смысле, что он не учитывает перекрытия между регионами источника и назначения. Если вы попытаетесь сместить объекты "вниз" в std::vector
с std::copy
, вы испортите данные. Есть ли аналог алгоритма STL memmove
справиться с такими ситуациями? Или я должен свернуть свой собственный с обратными итераторами?
4 ответа
Он не обрабатывает перекрывающиеся диапазоны, если начало выходного диапазона перекрывается с входным диапазоном.
К счастью, вы можете использовать std::copy_backward
вместо этого (что требует, чтобы вы не перекрывали конец диапазона вывода с диапазоном ввода).
Предпосылки для std::copy
, запрещает перекрытие:
Прототип
template <class InputIterator, class OutputIterator> OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result);
Предпосылками
[first, last)
допустимый диапазон- результат не является итератором в пределах диапазона
[first, last)
,- Достаточно места для хранения всех копируемых элементов. Более формально требование состоит в том, что
[result, result + (last - first))
допустимый диапазон [1]
Проект стандарта C++17
В проекте стандарта C++17 n4659 говорится:
28.6.1 "Копировать":
template<class InputIterator, class OutputIterator> OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result);
1 Требуется: результат не должен быть в диапазоне [первый, последний).
2 Эффекты: копирует элементы из диапазона [first, last) в диапазон [result, result + (last - first)), начиная с первого и продолжая до последнего.
а также:
template<class BidirectionalIterator1, class BidirectionalIterator2> BidirectionalIterator2 copy_backward( BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 result);
17 Требуется: результат не должен быть в диапазоне (первый, последний).
18 Эффекты: копирует элементы из диапазона [first, last) в диапазон [result - (last-first), result), начиная с последней - 1 и продолжая до первой. (263) Для каждого положительного целого числа n <= (последний - первый) выполняет *(результат - n) = *(последний - n).
Затем в примечании объясняется, когда использовать copy_backward
:
263) copy_backward следует использовать вместо copy, когда last находится в диапазоне [result - (last - first), result)
Следовательно, для этих функций не требуется никакого перекрытия, и в отличие отmemcpy
поведение перекрытий четко определено в Effects
разделы.
Вы просто выбираете между ними, потому что вы обычно хотите std::copy
для копирования влево и std::copy_backward
за право копирования.
C++ также имеет дальний вариант std::move
в <algorithm>
который движется вместо копирования.
Кажется, самый простой способ - создать временный вектор диапазона, который вы хотите скопировать:
std::vector copiedRange( srcVecIterBegin, srcVecIterEnd);
std::copy( copiedRange.begin(), copiedRange.end(), srcVecIterCopyLocIter);
Вы можете обернуть это в шаблонную функцию, которая должна уметь делать перекрытие, используя любой тип контейнера / итератора.