Обрабатывает ли 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);

Вы можете обернуть это в шаблонную функцию, которая должна уметь делать перекрытие, используя любой тип контейнера / итератора.

Другие вопросы по тегам