C++
algorithms
copy_n
fill_n
generate_n

Why copy_n, fill_n and generate_n?

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

The C++ Standard Library provides copy_n, fill_n, and generate_n alongside their range-based counterparts copy, fill, and generate. The _n variants take an explicit count instead of an end iterator, making them essential when the number of elements is known but the end iterator is not readily available — common with output iterators, stream iterators, and raw pointers. They also express intent more clearly when you want to operate on exactly N elements.

copy_n

Copies exactly n elements from a source to a destination:

cpp
1#include <algorithm>
2#include <vector>
3#include <iostream>
4#include <iterator>
5
6int main() {
7    std::vector<int> src = {1, 2, 3, 4, 5, 6, 7, 8};
8    std::vector<int> dst(5);
9
10    // Copy first 5 elements
11    std::copy_n(src.begin(), 5, dst.begin());
12    // dst = {1, 2, 3, 4, 5}
13
14    // copy_n with output iterator (no end iterator available)
15    std::copy_n(src.begin(), 3, std::ostream_iterator<int>(std::cout, " "));
16    // Output: 1 2 3
17}

Why Not Just copy?

copy requires both begin and end iterators for the source range. copy_n only needs the begin iterator and a count:

cpp
1// With copy — need to compute end
2std::copy(src.begin(), src.begin() + 5, dst.begin());
3
4// With copy_n — just specify the count
5std::copy_n(src.begin(), 5, dst.begin());
6
7// With input iterators (like stream iterators), you may not have an end
8std::istream_iterator<int> in(std::cin);
9std::copy_n(in, 10, std::back_inserter(result));  // Read exactly 10 values
10// copy cannot do this without a sentinel — istream_iterator{} means "end of stream"

fill_n

Assigns a value to exactly n elements starting from an iterator:

cpp
1#include <algorithm>
2#include <vector>
3
4int main() {
5    std::vector<int> v(10, 0);
6
7    // Fill first 5 elements with 42
8    std::fill_n(v.begin(), 5, 42);
9    // v = {42, 42, 42, 42, 42, 0, 0, 0, 0, 0}
10
11    // Fill with back_inserter to grow the container
12    std::vector<int> v2;
13    std::fill_n(std::back_inserter(v2), 3, 99);
14    // v2 = {99, 99, 99}
15}

fill vs fill_n

cpp
1// fill — needs begin and end
2std::fill(v.begin(), v.begin() + 5, 42);
3
4// fill_n — needs begin and count
5std::fill_n(v.begin(), 5, 42);
6
7// fill_n is essential with output iterators that have no "end"
8std::fill_n(std::ostream_iterator<int>(std::cout, " "), 5, 0);
9// Output: 0 0 0 0 0

generate_n

Calls a generator function exactly n times, storing results starting from an iterator:

cpp
1#include <algorithm>
2#include <vector>
3#include <random>
4
5int main() {
6    std::vector<int> v(10);
7
8    // Generate sequential numbers
9    int counter = 0;
10    std::generate_n(v.begin(), 10, [&counter]() { return counter++; });
11    // v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
12
13    // Generate random numbers
14    std::mt19937 rng(42);
15    std::uniform_int_distribution<int> dist(1, 100);
16    std::generate_n(v.begin(), 10, [&]() { return dist(rng); });
17
18    // Generate Fibonacci sequence
19    int a = 0, b = 1;
20    std::generate_n(v.begin(), 10, [&]() {
21        int result = a;
22        int temp = a + b;
23        a = b;
24        b = temp;
25        return result;
26    });
27    // v = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34}
28}

generate vs generate_n

cpp
1// generate — fills the entire range
2std::generate(v.begin(), v.end(), generator);
3
4// generate_n — fills exactly n elements
5std::generate_n(v.begin(), 5, generator);
6
7// generate_n with back_inserter
8std::vector<int> v2;
9std::generate_n(std::back_inserter(v2), 5, [n=0]() mutable { return n++; });
10// v2 = {0, 1, 2, 3, 4}

When to Use _n Variants

ScenarioUse _n variantWhy
Output iterators (ostream, back_inserter)YesNo end iterator available
Stream iterators (istream)YesEnd = end of stream, but you want N items
Raw pointers without sizeYesCount is known, end pointer requires arithmetic
Array with known lengthEither_n is slightly more readable
Container with begin/endPrefer non-_nRange-based is more idiomatic

C++20 Ranges

C++20 provides ranges versions with additional safety:

cpp
1#include <ranges>
2#include <algorithm>
3
4std::vector<int> src = {1, 2, 3, 4, 5};
5std::vector<int> dst(3);
6
7// Ranges copy — takes a range, not begin/end
8std::ranges::copy(src | std::views::take(3), dst.begin());
9
10// Ranges fill
11std::ranges::fill(dst, 42);
12
13// Ranges generate (no generate_n in ranges, but you can combine)
14std::ranges::generate(dst, [n=0]() mutable { return n++; });

Return Values

The _n variants return an iterator past the last element written:

cpp
1std::vector<int> v(10, 0);
2
3auto it = std::fill_n(v.begin(), 5, 42);
4// it points to v[5] — the element after the last filled
5
6// Useful for chaining
7auto it2 = std::fill_n(it, 3, 99);
8// v = {42, 42, 42, 42, 42, 99, 99, 99, 0, 0}
9// it2 points to v[8]

Common Pitfalls

  • Buffer overflow: copy_n, fill_n, and generate_n do not check that the destination has enough space. Writing past the end of a container is undefined behavior. Ensure the destination is large enough or use back_inserter.
  • Negative or zero count: Passing n <= 0 to _n functions is well-defined — they simply do nothing and return the input iterator. But accidentally passing a negative count (e.g., from signed integer arithmetic) is a logic bug.
  • Stateful generators and copies: If you pass a lambda by value to generate_n, the lambda's state is copied. Use [&] capture or std::ref to ensure state is shared if needed.
  • Performance: The _n variants have identical performance to their range-based counterparts. The choice is about API ergonomics, not speed.
  • Prefer ranges in C++20: If targeting C++20, std::views::take(n) combined with std::ranges::copy is often more readable than copy_n.

Summary

  • Use copy_n, fill_n, generate_n when you have a count but no end iterator (output iterators, streams, raw pointers)
  • Use the range-based copy, fill, generate when you have both begin and end iterators
  • _n variants return an iterator past the last written element — useful for chaining operations
  • Always ensure the destination has enough capacity — _n functions do not bounds-check
  • In C++20, prefer std::ranges and std::views::take(n) for a safer, more expressive alternative

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.