20

Looking at n3092, in §6.5.4 we find the equivalency for a range-based for loop. It then goes on to say what __begin and __end are equal to. It differentiates between arrays and other types, and I find this redundant (aka confusing).

It says for arrays types that __begin and __end are what you expect: a pointer to the first and a pointer to one-past the end. Then for other types, __begin and __end are equal to begin(__range) and end(__range), with ADL. Namespace std is associated, in order to find the std::begin and std::end defined in <iterator>, §24.6.5.

However, if we look at the definition of std::begin and std::end, they are both defined for arrays as well as container types. And the array versions do exactly the same as above: pointer to the first, pointer to one-past the end.

Why is there a need to differentiate arrays from other types, when the definition given for other types would work just as well, finding std::begin and std::end?


Some abridged quotes for convenience:

§6.5.4 The range-based for statement

— 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.

— otherwise, begin-expr and end-expr are begin(__range) and end(__range), respectively, where begin and end are looked up with argument-dependent lookup (3.4.2). For the purposes of this name lookup, namespace std is an associated namespace.

§24.6.5 range access

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

Returns: array.

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

Returns: array + N.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
GManNickG
  • 494,350
  • 52
  • 494
  • 543

1 Answers1

23

This avoids a corner-case with ADL:

namespace other {
  struct T {};
  int begin(T*) { return 42; }
}

other::T a[3];
for (auto v : a) {}

Because ADL finds other::begin when calling begin(a), the equivalent code would break causing a confusing compile error (along the lines of "can't compare int to other::T*" as end(a) would return a T*) or different behavior (if other::end was defined and did something likewise unexpected).

  • 8
    Nice :) It also enables users to use the loop without including any headers first. – Johannes Schaub - litb Apr 15 '10 at 21:01
  • @Johannes: Shouldn't be a problem; if you include a container header, you get begin & end too, according to 24.6.5 in the FCD. Types not in the stdlib should make any custom begin/end available similarly, so only in exceedingly rare cases would it be required. (But, damn, that's a major annoyance if you run into it.) –  Sep 21 '10 at 05:23
  • @Roger iterating over an `int[]` is such a case where a header would be required then, i mean. – Johannes Schaub - litb Sep 21 '10 at 10:58
  • 4
    One line summary: looking for `begin` and `end` via ADL may not find `std::begin` and `std::end`. – Ben Voigt Nov 22 '10 at 01:09