Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does a "for each" loop in C++ know the length of an array

I was looking at the following example from http://www.cplusplus.com/doc/tutorial/arrays/ and I couldn't figure out how the 2nd for loop worked. How can the for loop know when the array ends. If it can figure this out why does the first loop not use a similar approach? It was my impression that the length of an array could not be determined. I'm not sure how to reconcile these notions. Thanks!

Edit: Thanks for all the great answers!

#include <iostream>
using namespace std;
int main()
{
  int myarray[3] = {10,20,30};

  for (int i=0; i<3; ++i)
    ++myarray[i];

  for (int elem : myarray)
    cout << elem << '\n';
}
like image 766
Bren Avatar asked Aug 10 '14 19:08

Bren


2 Answers

The reason this works is that the for loop effectively1 uses std::begin and std::end. Those, in turn, work, because they provide overloads specifically for built-in arrays, something like this:

template <class T, size_t N>
T *begin(T (&array)[N]) {
    return array;
}

template <class T, size_t N>
T *end(T (&array)[N]) {
    return array+N;
}

Although it (apparently) hadn't been realized before the original (1998) C++ standard was published, this technique doesn't require any language features beyond those available in C++98. C++11 codified the technique and puts it to use.

Since, in this case, the parameter is specified as a reference to an array, type deduction will only succeed when the parameter really is an array. In the case of std::begin, there are also versions that support other parameter types and use (for example) the begin() and end() members of collections, if that type is matched.


1. "Effectively" in this case meaning there are some circumstances in which a range-based for loop uses begin and end, and others in which they're not. If you're into technicalities, they're not used for arrays, but a similar computation is done directly. Likewise, for container types that have begin and end members, those are used directly. If neither of those is true, then begin(range) and end(range) are used, which can use either std::begin/std::end, or a begin(x)/end(x) pair found by argument dependent lookup.

like image 105
Jerry Coffin Avatar answered Nov 10 '22 09:11

Jerry Coffin


The compiler knows the number of elements of the array due to the array definition. So it uses expressions myarray and myarray + 3 to traverse the array.

In fact the loop looks the following way

for ( auto first = myarray, last = myarray + 3; first != last; ++first )
{
   auto elem = *first;
   cout << elem << '\n';
}

Take into account that the range-based for statement use neither std::begin() nor std::end() for arrays as others wrote here.:)

According to the C++ Standard

— if _RangeT is an array type, begin-expr and end-expr are __range and __range + __bound, respectively, where __bound is the array bound. If _RangeT is an array of unknown size or an array of incomplete type, the program is ill-formed;

like image 20
Vlad from Moscow Avatar answered Nov 10 '22 07:11

Vlad from Moscow