Why use non-member begin and end functions in C++11?
Every standard container has a begin
and end
method for returning iterators for that container. However, C++11 has apparently introduced free functions called std::begin
and std::end
which call the begin
and end
member functions. So, instead of writing
auto i = v.begin();
auto e = v.end();
you'd write
auto i = std::begin(v);
auto e = std::end(v);
In his talk, Writing Modern C++, Herb Sutter says that you should always use the free functions now when you want the begin or end iterator for a container. However, he does not go into detail as to why you would want to. Looking at the code, it saves you all of one character. So, as far as the standard containers go, the free functions seem to be completely useless. Herb Sutter indicated that there were benefits for non-standard containers, but again, he didn't go into detail.
So, the question is what exactly do the free function versions of std::begin
and std::end
do beyond calling their corresponding member function versions, and why would you want to use them?
Solution 1:
How do you call .begin()
and .end()
on a C-array ?
Free-functions allow for more generic programming because they can be added afterwards, on a data-structure you cannot alter.
Solution 2:
Consider the case when you have library that contain class:
class SpecialArray;
it has 2 methods:
int SpecialArray::arraySize();
int SpecialArray::valueAt(int);
to iterate over it's values you need to inherit from this class and define begin()
and end()
methods for cases when
auto i = v.begin();
auto e = v.end();
But if you always use
auto i = begin(v);
auto e = end(v);
you can do this:
template <>
SpecialArrayIterator begin(SpecialArray & arr)
{
return SpecialArrayIterator(&arr, 0);
}
template <>
SpecialArrayIterator end(SpecialArray & arr)
{
return SpecialArrayIterator(&arr, arr.arraySize());
}
where SpecialArrayIterator
is something like:
class SpecialArrayIterator
{
SpecialArrayIterator(SpecialArray * p, int i)
:index(i), parray(p)
{
}
SpecialArrayIterator operator ++();
SpecialArrayIterator operator --();
SpecialArrayIterator operator ++(int);
SpecialArrayIterator operator --(int);
int operator *()
{
return parray->valueAt(index);
}
bool operator ==(SpecialArray &);
// etc
private:
SpecialArray *parray;
int index;
// etc
};
now i
and e
can be legally used for iteration and accessing of values of SpecialArray
Solution 3:
Using the begin
and end
free functions adds one layer of indirection. Usually that is done to allow more flexibility.
In this case I can think of a few uses.
The most obvious use is for C-arrays (not c pointers).
Another is when trying to use a standard algorithm on a non-conforming container (ie the container is missing a .begin()
method). Assuming you can't just fix the container, the next best option is to overload the begin
function. Herb is suggesting you always use the begin
function to promote uniformity and consistency in your code. Instead of having to remember which containers support method begin
and which need function begin
.
As an aside, the next C++ rev should copy D's pseudo-member notation. If a.foo(b,c,d)
is not defined it instead tries foo(a,b,c,d)
. It's just a little syntactic sugar to help us poor humans who prefer subject then verb ordering.
Solution 4:
To answer your question, the free functions begin() and end() by default do nothing more than call the container's member .begin() and .end() functions. From <iterator>
, included automatically when you use any of the standard containers like <vector>
, <list>
, etc., you get:
template< class C >
auto begin( C& c ) -> decltype(c.begin());
template< class C >
auto begin( const C& c ) -> decltype(c.begin());
The second part of you question is why prefer the free functions if all they do is call the member functions anyway. That really depends on what kind of object v
is in your example code. If the type of v is a standard container type, like vector<T> v;
then it doesn't matter if you use the free or member functions, they do the same thing. If your object v
is more generic, like in the following code:
template <class T>
void foo(T& v) {
auto i = v.begin();
auto e = v.end();
for(; i != e; i++) { /* .. do something with i .. */ }
}
Then using the member functions breaks your code for T = C arrays, C strings, enums, etc. By using the non-member functions, you advertise a more generic interface that people can easily extend. By using the free function interface:
template <class T>
void foo(T& v) {
auto i = begin(v);
auto e = end(v);
for(; i != e; i++) { /* .. do something with i .. */ }
}
The code now works with T = C arrays and C strings. Now writing a small amount of adapter code:
enum class color { RED, GREEN, BLUE };
static color colors[] = { color::RED, color::GREEN, color::BLUE };
color* begin(const color& c) { return begin(colors); }
color* end(const color& c) { return end(colors); }
We can get your code to be compatible with iterable enums too. I think Herb's main point is that using the free functions is just as easy as using the member functions, and it gives your code backward compatibility with C sequence types and forward compatibility with non-stl sequence types (and future-stl types!), with low cost to other developers.