Appending a vector to a vector
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
The most common way to append one vector to another in C++ is vec1.insert(vec1.end(), vec2.begin(), vec2.end()). This copies all elements from vec2 to the end of vec1 in a single operation. For move semantics, use std::make_move_iterator. For pre-allocation, call vec1.reserve() before the insert. This article covers every method available in modern C++ (C++11 through C++23), their performance characteristics, and the trade-offs between them.
Method 1: insert (The Standard Approach)
The insert member function is the most idiomatic way to append one vector to another.
The range-based insert handles memory allocation internally. If vec1 does not have enough capacity, it reallocates once and copies everything. This makes it both simple and efficient.
Method 2: insert with reserve (Optimal Performance)
If you know the combined size ahead of time, calling reserve before insert guarantees at most one reallocation.
In practice, most standard library implementations of insert already calculate the needed capacity when given random-access iterators. But calling reserve explicitly makes your intent clear and guarantees the behavior regardless of implementation.
Method 3: std::copy with back_inserter
The std::copy algorithm combined with std::back_inserter achieves the same result through a different mechanism.
The reserve call is more important here than with insert. Without it, back_inserter calls push_back for each element individually, which can trigger multiple reallocations. With reserve, it is a single allocation followed by N copy operations.
Method 4: Move semantics (C++11)
When you no longer need the source vector's elements, moving avoids expensive copies. This matters for vectors of objects like std::string or large structs.
After the move, vec2 still has the same number of elements, but each element is in a moved-from state. For std::string, that typically means each element is now an empty string.
Method 5: append_range (C++23)
C++23 introduces append_range, which provides the cleanest syntax.
This is equivalent to the range-based insert but reads more naturally. It works with any range, not just vectors.
Method 6: Concatenation into a new vector
Sometimes you want to leave both source vectors unchanged and produce a new combined vector.
The reserve call ensures exactly one allocation for the combined vector.
Comparison of Methods
| Method | Copies/Moves | Requires Reserve | C++ Standard | Best For |
insert(end, begin, end) | Copies | Optional (often optimized) | C++98 | General use |
insert + reserve | Copies | Yes (explicit) | C++98 | Performance-critical code |
std::copy + back_inserter | Copies | Recommended | C++98 | Algorithm-based pipelines |
insert + make_move_iterator | Moves | Optional | C++11 | Expensive-to-copy elements |
append_range | Copies | Optional | C++23 | Modern codebases |
| Two inserts into new vector | Copies | Recommended | C++98 | Preserving both source vectors |
Performance Considerations
Reallocation Cost
Vector reallocation involves allocating new memory, copying (or moving) all existing elements, and freeing the old memory. For a vector of N elements, each reallocation is O(N). Without reserve, appending M elements could trigger O(log M) reallocations because vectors typically grow by a factor of 1.5x or 2x.
Benchmark: insert vs push_back Loop
Appending 1 million integers using different methods:
The insert approach is generally faster because it can use memcpy or memmove for trivially copyable types, while push_back operates element by element.
Exception Safety
The range-based insert provides the strong exception guarantee for trivially copyable types and the basic exception guarantee otherwise. If an exception is thrown during copying, the vector is left in a valid but potentially partially-extended state. Using reserve beforehand ensures no reallocation happens during the insert, which simplifies the exception behavior.
Appending Vectors of Different Types
If the source and destination vectors have different but convertible element types, use insert with implicit conversion:
This works because int is implicitly convertible to double. For types that are not implicitly convertible, use std::transform instead.
Self-Append: Appending a Vector to Itself
Appending a vector to itself requires care because the source iterators may be invalidated by reallocation.
Some standard library implementations handle self-append correctly, but the behavior is not guaranteed by the standard. Make a copy of the source data first to be safe.
Common Pitfalls
- Using a push_back loop instead of insert. A manual loop with
push_backis slower than range-basedinsertbecauseinsertcan optimize the allocation and use bulk memory operations. - Forgetting reserve when using back_inserter. Without
reserve,std::copywithback_insertertriggers multiple reallocations. This is slower than a singlereservefollowed by the copy. - Self-appending without a copy. Inserting a vector's own elements into itself can invalidate iterators during reallocation. Always copy the source data first.
- Not using move semantics for expensive types. Appending vectors of strings, maps, or other complex objects should use
make_move_iteratorwhen the source is no longer needed. This avoids deep copies. - Assuming vec2 is empty after a move. After
insertwith move iterators,vec2retains its size. Each element is in a moved-from state (valid but unspecified), butvec2itself is not empty. Callvec2.clear()explicitly if needed. - Ignoring append_range in C++23 codebases. If your project targets C++23,
append_rangeis cleaner and more expressive than theinsert(end, begin, end)pattern.
Summary
- Use
vec1.insert(vec1.end(), vec2.begin(), vec2.end())as the standard approach for appending one vector to another. - Call
vec1.reserve(vec1.size() + vec2.size())before insertion for guaranteed single-allocation performance. - Use
std::make_move_iteratorwhen the source vector's elements are expensive to copy and the source is no longer needed. - In C++23,
vec1.append_range(vec2)provides the cleanest syntax. - Avoid manual
push_backloops. Range-basedinsertenables bulk memory optimizations that element-by-element insertion cannot match. - Never self-append without first copying the source data, as reallocation can invalidate the source iterators.

