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 viastd::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_listandstd::unordered_setprovide 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
*itreturns a real reference (T&). It may return a temporary proxy or a value by-value (as long as*itis convertible to the iterator'svalue_type). Because of this, you cannot rely on the identity of the underlying object. - Forward Iterator: Guarantees that
*itreturns a true reference:const value_type&for constant iterators, orvalue_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
InputIteratorandOutputIteratorrequirements, allowing algorithms likestd::replaceto 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 |