One benefit of std::begin and std::end is that they serve as extension points
for implementing standard interface for external classes.
If you'd like to use CustomContainer class with range-based for loop or template
function which expects .begin() and .end() methods, you'd obviously have to
implement those methods.
If the class does provide those methods, that's not a problem. When it doesn't,
you'd have to modify it*.
This is not always feasible, for example when using external library, esspecially
commercial and closed source one.
In such situations, std::begin and std::end come in handy, since one can provide
iterator API without modifying the class itself, but rather overloading free functions.
Example: suppose that you'd like to implement count_if function that takes a container
instead of a pair of iterators. Such code might look like this:
template<typename ContainerType, typename PredicateType>
std::size_t count_if(const ContainerType& container, PredicateType&& predicate)
{
using std::begin;
using std::end;
return std::count_if(begin(container), end(container),
std::forward<PredicateType&&>(predicate));
}
Now, for any class you'd like to use with this custom count_if, you only have
to add two free functions, instead of modifying those classes.
Now, C++ has a mechanisim called Argument Dependent Lookup
(ADL), which makes such approach even more flexible.
In short, ADL means, that when a compiler resolves an unqualified function (i. e.
function without namespace, like begin instead of std::begin), it will also
consider functions declared in namespaces of its arguments. For example:
namesapce some_lib
{
// let's assume that CustomContainer stores elements sequentially,
// and has data() and size() methods, but not begin() and end() methods:
class CustomContainer
{
...
};
}
namespace some_lib
{
const Element* begin(const CustomContainer& c)
{
return c.data();
}
const Element* end(const CustomContainer& c)
{
return c.data() + c.size();
}
}
// somewhere else:
CustomContainer c;
std::size_t n = count_if(c, somePredicate);
In this case, it doesn't matter that qualified names are some_lib::begin and some_lib::end
- since CustomContainer is in some_lib:: too, compiler will use those overloads in count_if.
That's also the reason for having using std::begin; and using std::end; in count_if.
This allows us to use unqualified begin and end, therefore allowing for ADL and
allowing compiler to pick std::begin and std::end when no other alternatives are found.
We can eat the cookie and have the cookie - i. e. have a way to provide custom implementation
of begin/end while the compiler can fall back to standard ones.
Some notes:
For the same reason, there are other similar functions: std::rbegin/rend,
std::size and std::data.
As other answers mentions, std:: versions have overloads for naked arrays. That's useful,
but is simply a special case of what I've described above.
Using std::begin and friends is particularly good idea when writing template code,
because this makes those templates more generic. For non-template you might just
as well use methods, when applicable.
P. S. I'm aware that this post is nearly 7 years old. I came across it because I wanted to
answer a question which was marked as a duplicate and discovered that no answer here mentions ADL.