Why do I have to always specify the range in STL's algorithm functions explicitly, even if I want to work on the whole container?

Solution 1:

Most of the time, the standard library is designed to provide the minimal interface necessary to accomplish all the tasks required, i.e. it tries to avoid interface bloat. You can operate on a whole container when the algorithm accepts a pair of iterators, but you could not operate on a subrange if the algorithm accepted a container. So the iterator pair is more fundamental, and so that's what the standard library provides. Convenience functions are usually not included.

However, you're certainly not the first person to think this way, and there's the entire Boost.Range library devoted to treating a range (both a container and an arbitrary range) as a single entity instead of a pair of iterators.

There is also a formal proposal to incorporate Eric Niebler's range library in a future version of the C++ standard library.

Solution 2:

This is because STL algorithms are container-independent. Iterators provide a uniform way for them to work, with the only limitation being what are the guarantees this algorithm requires from these iterators.

For example, if you want to do a linear search for min_element(), you only need forward iterators (i.e. they only have to support operator++). So, you can write one simple templated implementation that will work with essentially every container, despite how the container is implemented under the hood.

You could overload functions to take only the container and apply begin() and end() on them, but this would mean that you have one more interface to remember.

Edit

I suppose there are a few other arguments that could be made. Since STL was all about mathematical beauty and emphasis that algorithms are separate from containers, always passing iterators would reinforce this notion.

On the other hand, in terms of the C++ language as a whole, one of the main goals of Stroustrup was to educate developers. The full power of STL algorithms comes from the ability to pass arbitrary iterator ranges, but most of the time you want to operate on the whole container. If you provided overloads for the whole container, it could be argued that a large number of people would never bother to learn to use range versions, because it would be precisely those versions that would fall into "another interface to remember" category.