Skip to main content

Boost.Iterator

Boost.Iterator provides tools for building and adapting iterators without the boilerplate that the standard iterator interface demands. Its two core pieces are iterator_facade — which generates a complete iterator from a handful of primitives — and iterator_adaptor — which wraps an existing iterator and overrides specific operations.

The problem it solves

Writing a correct STL iterator by hand means implementing five nested types, up to seven operators, and getting the const/non-const interplay right. A single oversight causes hard-to-diagnose compilation failures. iterator_facade reduces this to three or four simple functions; the library synthesises the rest.

iterator_facade: build a custom iterator

You inherit from iterator_facade and supply a few core operations. The library generates all the operators (++, --, *, ->, ==, !=, <, +=, ...) from those primitives.

node_iterator.cpp
#include <boost/iterator/iterator_facade.hpp>
#include <iostream>

struct Node {
int value;
Node* next;
};

class NodeIterator
: public boost::iterator_facade<NodeIterator, int, boost::forward_traversal_tag>
{
public:
NodeIterator() : node_(nullptr) {}
explicit NodeIterator(Node* n) : node_(n) {}

private:
friend class boost::iterator_core_access;

void increment() { node_ = node_->next; }
bool equal(const NodeIterator& other) const { return node_ == other.node_; }
int& dereference() const { return node_->value; }

Node* node_;
};

int main() {
Node c{3, nullptr};
Node b{2, &c};
Node a{1, &b};

for (NodeIterator it(&a); it != NodeIterator(); ++it) {
std::cout << *it << " "; // 1 2 3
}
std::cout << "\n";
}

The three functions — increment, equal, dereference — are all you need for a forward iterator. Add decrement for bidirectional, plus advance and distance_to for random access.

Traversal tags

Boost.Iterator uses its own traversal tag hierarchy (incrementable, single_pass, forward, bidirectional, random_access) that is finer-grained than the standard iterator categories. The library maps them to std::iterator_traits categories automatically.

iterator_adaptor: wrap and customise

When you want to modify the behaviour of an existing iterator — skip certain elements, transform values, change traversal — iterator_adaptor lets you override just the parts that differ:

stride_iterator.cpp
#include <boost/iterator/iterator_adaptor.hpp>
#include <vector>
#include <iostream>

template <typename BaseIter>
class StrideIterator
: public boost::iterator_adaptor<StrideIterator<BaseIter>, BaseIter>
{
public:
StrideIterator(BaseIter it, int stride)
: StrideIterator::iterator_adaptor_(it), stride_(stride) {}

private:
friend class boost::iterator_core_access;
void increment() { std::advance(this->base_reference(), stride_); }
int stride_;
};

int main() {
std::vector<int> v{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

StrideIterator begin(v.begin(), 3);
StrideIterator end(v.end(), 3);

for (auto it = begin; it.base() < end.base(); ++it) {
std::cout << *it << " "; // 0 3 6 9
}
std::cout << "\n";
}

Ready-made iterator adaptors

Boost.Iterator ships several commonly needed adaptors out of the box:

AdaptorPurposeExample use
counting_iteratorGenerates integers: 0, 1, 2, ...Loop without a container
transform_iteratorApplies a function on dereferenceRead-only projection
filter_iteratorSkips elements that fail a predicateIterate matching items
zip_iteratorIterates multiple ranges in lockstepParallel traversal
reverse_iteratorReverses traversal directionBackward iteration
indirect_iteratorDereferences through pointersIterate vector<T*> as T&
counting_zip.cpp
#include <boost/iterator/counting_iterator.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <vector>
#include <iostream>

int main() {
// counting_iterator: generate integers without a container
boost::counting_iterator<int> from(0), to(5);
for (auto it = from; it != to; ++it) {
std::cout << *it << " "; // 0 1 2 3 4
}
std::cout << "\n";

// transform_iterator: project on the fly
std::vector<int> v{1, 2, 3, 4};
auto square = [](int n) { return n * n; };
auto begin = boost::make_transform_iterator(v.begin(), square);
auto end = boost::make_transform_iterator(v.end(), square);
for (auto it = begin; it != end; ++it) {
std::cout << *it << " "; // 1 4 9 16
}
std::cout << "\n";
}

filter_iterator

Wraps an existing iterator and skips elements that do not satisfy a predicate:

filter_iter.cpp
#include <boost/iterator/filter_iterator.hpp>
#include <vector>
#include <iostream>

int main() {
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8};
auto is_even = [](int n) { return n % 2 == 0; };

auto begin = boost::make_filter_iterator(is_even, v.begin(), v.end());
auto end = boost::make_filter_iterator(is_even, v.end(), v.end());

for (auto it = begin; it != end; ++it) {
std::cout << *it << " "; // 2 4 6 8
}
std::cout << "\n";
}
filter_iterator needs the end

make_filter_iterator requires the end iterator as a third argument so it knows where to stop skipping. Forgetting it is a compilation error — not a silent bug.

See also