Difference Between Input and Forward Iterators in C++

In the legacy iterator hierarchy of C++, both input iterators and forward iterators are used to traverse sequences and read data, but they differ significantly in their traversal capabilities, state preservation, and dereferencing behavior. While an input iterator supports only single-pass, read-only algorithms, a forward iterator refines the input iterator concept to support multi-pass traversal, making it capable of saving positions and repeatedly iterating over the same sequence.

Core Differences

1. Single-Pass vs. Multi-Pass Traversal

The most critical distinction lies in whether an iterator can be used in multi-pass algorithms:

  • Input Iterator (Single-Pass): Designed for single-pass consumption of data. Once an input iterator is incremented (++it), all copies of the previous iterator value become invalid for dereferencing. This makes them ideal for streams where data is consumed on the fly, such as reading from standard input via std::istream_iterator.
  • Forward Iterator (Multi-Pass): Satisfies the multipass guarantee. Incrementing a forward iterator does not invalidate copies of earlier positions. You can store an iterator at a specific point in a sequence, advance another iterator forward, and still safely return to and dereference the stored iterator. Collections like std::forward_list and std::unordered_set provide forward iterators.

2. Dereferencing and Reference Types

How values are accessed through the dereference operator (*) differs between the two categories:

  • Input Iterator: Does not guarantee that *it returns a real reference (T&). It may return a temporary proxy or a value by-value (as long as *it is convertible to the iterator's value_type). Because of this, you cannot rely on the identity of the underlying object.
  • Forward Iterator: Guarantees that *it returns a true reference: const value_type& for constant iterators, or value_type& for mutable forward iterators. Consequently, if two forward iterators compare equal, dereferencing them refers to the exact same memory location.

3. Default Constructibility

  • Input Iterator: In C++98/03 legacy requirements, an input iterator was not required to be default-constructible (though many implementations provided it).
  • Forward Iterator: Must always be default-constructible. A default-constructed forward iterator represents a "singular" value that can be assigned to, but not dereferenced.

4. Read vs. Write Capabilities

  • Input Iterator: Strictly read-only relative to the input sequence. You cannot modify the underlying elements through an input iterator.
  • Forward Iterator: Can be either constant (read-only) or mutable (read/write). A mutable forward iterator satisfies both the InputIterator and OutputIterator requirements, allowing algorithms like std::replace to read and write elements in place.

Summary Comparison

Feature Input Iterator Forward Iterator
Pass Capability Single-pass only Multi-pass
Equality Comparison Compared mainly to detect end-of-stream Can compare any two iterators in the same range
Dereference Result Value or proxy convertible to value_type True reference (T& or const T&)
State Invalidation Incrementing invalidates earlier copies Earlier copies remain valid
Canonical Example std::istream_iterator std::forward_list::iterator