Why use non-member begin and end functions in C++11?

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.


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


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.