begin, end annoyance in STL algorithms
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
The repeated begin() and end() calls in classic STL code annoy many C++ developers, but they come from a deliberate design choice. Pre-ranges STL algorithms operate on iterator pairs rather than on containers, because that lets the same algorithm work on whole containers, subranges, arrays, and custom iterator types.
Why STL Algorithms Take Iterator Pairs
The original STL abstraction is not “container.” It is “range.” A range is represented by two iterators: the first element and one-past-the-last element.
That choice gives algorithms broad flexibility. The same algorithm call style can operate on:
- a whole
vector - a slice inside a
vector - a raw array
- a custom range represented only by iterators
The repeated typing is real, but so is the generic power.
Prefer std::begin and std::end in Generic Code
Inside templates, using std::begin and std::end is better than calling member functions directly because it also works with raw arrays and other types that provide the appropriate overloads.
This does not remove the two-iterator syntax, but it does make the code more generic and more idiomatic.
Small Helpers Can Reduce Noise
Many codebases define a tiny helper macro to shorten whole-container calls.
This is pragmatic, especially in competitive programming or short-lived code. The downside is that macros hide arguments and can make debugging or refactoring slightly less transparent.
For long-lived production code, some teams prefer explicit iterator pairs for clarity, while others accept the macro because it removes boilerplate. That is mostly a style trade-off.
Why a Helper Function Is Harder
A normal helper function cannot replace ALL(c) perfectly because the classic STL algorithm interface expects two separate iterator arguments, not a single range object. That limitation is one reason the old syntax remained annoying for so long.
You can still wrap whole operations in your own helper function, but that changes the interface rather than just shortening it.
C++20 Ranges Are the Real Fix
If your codebase supports C++20, std::ranges algorithms are the cleanest answer.
This keeps the generic spirit of the STL while removing most of the repetitive begin/end ceremony for whole-container operations.
Why the Old Design Still Matters
Even if ranges are nicer, the old iterator-pair design still explains a lot of existing code and libraries. It also explains why so many STL algorithms naturally support subranges.
For example:
That style is easy because the algorithm thinks in ranges, not in containers. So the annoyance and the power come from the same design decision.
Choose the Right Style for the Codebase
If you are working in C++11 or C++14, explicit iterator pairs remain normal and reasonable. Use std::begin and std::end in templates, and introduce shorthand only if the team agrees on it.
If you are working in C++20, prefer ranges algorithms where practical instead of inventing homegrown wrappers around the older interface.
The goal is not to resent the STL design. It is to use the best expression available at your language level.
Common Pitfalls
- Calling
c.begin()in generic code whenstd::begin(c)would support more types. - Hiding algorithm arguments behind large or opaque macros.
- Forgetting that classic STL algorithms are designed to work on subranges, not just whole containers.
- Mixing iterators from different containers by mistake.
- Rebuilding a private wrapper layer when
std::rangesis already available.
Summary
- Classic STL algorithms use iterator pairs because they were designed around generic ranges.
- '
std::beginandstd::endare the right default tools in generic helper code.' - A tiny
ALL(c)macro can be pragmatic, but it is only a style shortcut. - C++20
std::rangesalgorithms are the cleanest modern way to avoid repeatedbegin/end. - Choose the style that matches your language level and the needs of the codebase.

