How to Add One Vector to Another in C++: What push_back Hides

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: What the Documentation Doesn't Tell You



Imagine you're writing a high-load server or a data processing algorithm. You have two std::vector<int>s, and you need to merge them into one. It seems simple enough, right? Just add them. But C++ doesn't forgive a superficial approach. One wrong method, and you get unnecessary copies, performance drops, or even undefined behavior.



The question "Appending a vector to a vector" on Stack Overflow has gathered thousands of views for a reason. Even experienced developers sometimes confuse push_back with insert or forget about move semantics. In this article, we'll look under the compiler's hood and understand how adding one vector to another actually works. You'll learn why std::copy can be faster than insert, and when move semantics can save your project.



The Most Obvious (and Dangerous) Way: push_back in a Loop



Beginners often write code like this:



std::vector<int> dest = {1, 2, 3};std::vector<int> src = {4, 5, 6};

for (size_t i = 0; i < src.size(); ++i) { dest.push_back(src[i]);}


What's happening here? On each iteration, push_back checks if enough memory has been allocated. If not, a reallocation occurs: the old array is copied to a new memory area, and the old one is deleted. With three elements, there might be 2–3 reallocations. For small vectors, this is unnoticeable, but with thousands of elements, it's a disaster.



The problem: O(n) checks and potential copies of the entire array. In C++17 and later, the compiler may optimize this, but relying on it is risky.



What Happens Under the Hood of push_back?

  • Check size() < capacity().
  • If not, call reallocate(new_capacity).
  • Copy (or move) existing elements.
  • Delete the old buffer.
  • Copy the new element.


Conclusion: never use a loop to add an entire vector, unless you are doing it intentionally for a small number of elements.



The Right Way: std::vector::insert



The standard library provides an elegant solution:



std::vector<int> dest = {1, 2, 3};std::vector<int> src = {4, 5, 6};

dest.insert(dest.end(), src.begin(), src.end());


A one-liner! But what's hidden behind this call? insert does the following:

  • Calculates the required amount of memory: dest.size() + src.size().
  • If the current capacity is insufficient, it allocates exactly what is needed (or with a margin, depending on the implementation).
  • Shifts existing elements (if inserting not at the end).
  • Copies (or moves) new elements from src.


Important: insert performs one reallocation (or none if there is enough space). This is already better than a loop, but there is still copying of elements from src.



Performance of insert

If src is no longer needed after insertion, you can move its elements using std::make_move_iterator:



dest.insert(dest.end(),            std::make_move_iterator(src.begin()),            std::make_move_iterator(src.end()));


This turns copying into moving. For std::vector<int> there is no difference (int is a trivial type), but for std::vector<std::string> or complex objects, moving is cheap compared to copying.



Alternative: std::copy with back_inserter



Some developers prefer STL algorithms:





                
                

Blogs

Book Recommendations