C++
std::copy
overlapping ranges
algorithms
programming tips

Does stdcopy handle overlapping ranges?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

std::copy does not safely handle overlapping ranges when the destination overlaps with the source in the forward direction. Using std::copy with overlapping ranges where the destination is within or ahead of the source produces undefined behavior. For overlapping ranges, use std::copy_backward (when the destination is after the source) or std::move/std::memmove for raw memory.

std::copy Signature

cpp
template<class InputIt, class OutputIt>
OutputIt copy(InputIt first, InputIt last, OutputIt d_first);
  • Parameters:
    • first, last: Iterators to the initial and final position in the source range
    • d_first: Iterator to the beginning of the destination range
  • Return Value: An iterator to the element past the last element copied

When Overlapping Is Dangerous

cpp
1#include <algorithm>
2#include <vector>
3
4std::vector<int> v = {1, 2, 3, 4, 5};
5
6// UNDEFINED BEHAVIOR: destination overlaps source (forward direction)
7std::copy(v.begin(), v.begin() + 3, v.begin() + 1);
8// Intended: {1, 1, 2, 3, 5}
9// Actual: may produce {1, 1, 1, 1, 5} — each copy overwrites the next source

The problem: std::copy copies left to right. When d_first is inside [first, last), earlier copies overwrite values that later copies need to read.

Safe Overlapping with std::copy_backward

std::copy_backward copies from right to left, which is safe when the destination is after the source:

cpp
1#include <algorithm>
2#include <vector>
3
4std::vector<int> v = {1, 2, 3, 4, 5};
5
6// SAFE: copy_backward for destination after source
7std::copy_backward(v.begin(), v.begin() + 3, v.begin() + 4);
8// Result: {1, 1, 2, 3, 5} — correct!

When to Use Which

cpp
1// Source: [first, last)
2// Destination starts at d_first
3
4// If d_first < first (destination before source):
5std::copy(first, last, d_first);         // SAFE
6
7// If d_first > first and d_first < last (destination inside source):
8std::copy_backward(first, last, d_last); // SAFE
9
10// If d_first >= last (no overlap):
11std::copy(first, last, d_first);         // SAFE
ScenarioSafe Function
No overlapstd::copy or std::copy_backward
Destination before sourcestd::copy
Destination after source (overlap)std::copy_backward
Destination inside source (forward)std::copy_backward

std::memmove for Raw Memory

For raw memory (C-style arrays, trivially copyable types), std::memmove handles all overlapping cases:

cpp
1#include <cstring>
2
3int arr[] = {1, 2, 3, 4, 5};
4
5// memmove safely handles any overlap direction
6std::memmove(arr + 1, arr, 3 * sizeof(int));
7// Result: {1, 1, 2, 3, 5}

std::memmove vs std::memcpy:

  • std::memcpy: undefined for overlapping ranges (like std::copy)
  • std::memmove: safe for overlapping ranges (uses temporary buffer or reverse copy internally)

std::move for Move Semantics

std::move (the algorithm, not std::move for rvalue references) moves elements instead of copying:

cpp
1#include <algorithm>
2
3// Move elements (same overlap restrictions as std::copy)
4std::move(v.begin(), v.begin() + 3, v.begin() + 1);
5// UNDEFINED if overlapping forward — use std::move_backward

Practical Examples

Shifting Elements Right

cpp
1std::vector<int> v = {1, 2, 3, 4, 5};
2
3// Shift elements 0-2 one position to the right
4std::copy_backward(v.begin(), v.begin() + 3, v.begin() + 4);
5v[0] = 0;  // fill the gap
6// Result: {0, 1, 2, 3, 5}

Shifting Elements Left

cpp
1std::vector<int> v = {1, 2, 3, 4, 5};
2
3// Shift elements 2-4 one position to the left
4std::copy(v.begin() + 2, v.end(), v.begin() + 1);
5v.pop_back();
6// Result: {1, 3, 4, 5}

Insert Into a Sorted Array

cpp
1std::vector<int> v = {1, 3, 5, 7, 0};  // 0 is placeholder
2int insert_val = 4;
3
4auto pos = std::lower_bound(v.begin(), v.end() - 1, insert_val);
5std::copy_backward(pos, v.end() - 1, v.end());
6*pos = insert_val;
7// Result: {1, 3, 4, 5, 7}

Common Pitfalls

  • Assuming std::copy is safe: Unlike memmove, std::copy does not handle overlapping ranges. Always check if source and destination overlap before choosing the algorithm.
  • Wrong direction: std::copy_backward requires d_last (end of destination), not d_first. It copies from [first, last) to [d_last - (last - first), d_last).
  • Non-trivial types: std::memmove only works for trivially copyable types (PODs, primitive types). For objects with constructors/destructors, use std::copy_backward.
  • Off-by-one: std::copy_backward(first, last, d_last) copies last - first elements ending at d_last. Ensure d_last points one past the end of the destination.
  • Range validation: Neither std::copy nor std::copy_backward checks that the destination has enough space. Writing past the end of a container is undefined behavior.

Summary

  • std::copy does not handle overlapping ranges — undefined behavior if destination is inside the source range
  • Use std::copy_backward when the destination is after (and overlaps with) the source
  • Use std::memmove for safe overlapping copies of trivially copyable types
  • std::memcpy and std::copy have the same restriction: no overlap allowed
  • Always analyze the overlap direction to choose the correct algorithm

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.