Using std::back_inserter as an Output Iterator in C++

Populating dynamic collections in C++ often requires bridging standard algorithms with containers that grow on demand. This article explains the interaction between output iterators and std::back_inserter, detailing how the helper creates a std::back_insert_iterator, how iterator assignment translates to container growth, and how standard algorithms utilize this mechanism safely without pre-allocated memory.

The Role of Output Iterators in C++

In the C++ Standard Template Library (STL), algorithms operate on iterator abstractions rather than concrete container types. An output iterator is a fundamental iterator category designed for write-only, sequential data flow. It satisfies a minimal set of operations:

  • Dereferencing for assignment: *it = value writes a value to the current position.
  • Increment: ++it or it++ advances the iterator to the next position.

Standard output algorithms like std::copy, std::transform, or std::generate rely strictly on this protocol. When writing to a regular container iterator (such as vec.begin()), the container must already contain pre-allocated elements. Writing past the bounds invokes undefined behavior.

What std::back_inserter Does

std::back_inserter is a convenience function template defined in the <iterator> header. It constructs and returns a std::back_insert_iterator<Container> bound to the target container.

Instead of writing to pre-existing memory locations, std::back_insert_iterator adapts the output iterator interface to append elements to the end of the collection dynamically.

How the Interaction Works

The interaction between an algorithm and std::back_inserter hinges on operator overloading within std::back_insert_iterator.

  1. Proxy Dereference: When an algorithm executes *it, the iterator does not return a reference to an element. Instead, it returns a reference to the iterator itself (*this).
  2. Assignment via push_back: When the algorithm assigns a value using *it = value, the iterator's overloaded assignment operator intercepts the value. The operator implementation calls container->push_back(value) on the underlying container, forwarding either an lvalue or an rvalue.
  3. No-Op Increments: When the algorithm calls ++it or it++, the iterator does nothing and simply returns *this. Advancing is unnecessary because each assignment automatically increases the container's size and appends to the new end.
#include <vector>
#include <algorithm>
#include <iterator>

int main() {
    std::vector<int> source = {1, 2, 3, 4, 5};
    std::vector<int> destination; // Empty vector

    // std::copy acts on the output iterator interface
    // std::back_inserter adapts this interface to call destination.push_back()
    std::copy(source.begin(), source.end(), std::back_inserter(destination));

    return 0;
}

Container Requirements

Because std::back_insert_iterator relies directly on the push_back() member function, std::back_inserter can only be used with sequence containers that provide this method.

  • Supported: std::vector, std::deque, std::list, std::string
  • Unsupported: std::array (fixed size), std::forward_list (lacks back operations), and associative containers like std::set or std::map (which require std::inserter instead).

Through this mechanism, std::back_inserter allows standard algorithms to populate expanding collections safely, cleanly decoupling collection growth from algorithmic traversal.