Избыточность определения оператора на основе диапазона

Глядя на n3092, в §6.5.4 мы находим эквивалентность цикла for на основе диапазона. Затем он продолжает говорить, чему равны __begin и __end. Он различает массивы и другие типы, и я нахожу это излишним (также сбивающим с толку).

В нем говорится, что для типов массивов __begin и __end - это то, что вы ожидаете: указатель на первый и указатель на один после конца. Тогда для других типов __begin и __end равны begin(__range) и end(__range) с ADL. Пространство имен std связано, чтобы найти std::begin и std::end, определенные в <iterator>, §24.6.5.

Однако, если мы посмотрим на определение std::begin и std::end, они оба определены как для массивов, так и для типов контейнеров. И версии массива делают то же самое, что и выше: указатель на первый, указатель на один после конца.

Зачем нужно отличать массивы от других типов, когда определение, данное для других типов, будет работать так же хорошо, находя std::begin и std::end?


Некоторые сокращенные цитаты для удобства:

§6.5.4 Оператор for на основе диапазона

— если _RangeT — это тип массива, то begin-expr и end-expr — это __range и __range + __bound соответственно, где __bound — это граница массива. Если _RangeT является массивом неизвестного размера или массивом неполного типа, программа неправильно сформирована.

— в противном случае begin-expr и end-expr — это begin(__range) и end(__range) соответственно, где begin и end ищутся с помощью поиска, зависящего от аргумента (3.4.2). Для целей поиска этого имени пространство имен std является ассоциированным пространством имен.

§24.6.5 доступ к диапазону

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

Возвращает: массив.

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

Возвращает: массив + N.


person GManNickG    schedule 15.04.2010    source источник


Ответы (1)


Это позволяет избежать углового случая с ADL:

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

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

Поскольку ADL находит other::begin при вызове begin(a), эквивалентный код сломается, вызывая запутанную ошибку компиляции (в духе «невозможно сравнить int с other::T*», поскольку end(a) вернет T*) или другое поведение (если был определен other::end и сделал что-то неожиданное).

person Community    schedule 15.04.2010
comment
Приятно :) Это также позволяет пользователям использовать цикл без предварительного включения каких-либо заголовков. - person Johannes Schaub - litb; 16.04.2010
comment
@Johannes: не должно быть проблем; если вы включаете заголовок контейнера, вы также получаете начало и конец в соответствии с 24.6.5 в FCD. Типы, отсутствующие в stdlib, должны сделать любое пользовательское начало/конец доступным аналогичным образом, поэтому это потребуется только в чрезвычайно редких случаях. (Но, черт возьми, это сильно раздражает, если вы столкнетесь с этим.) - person ; 21.09.2010
comment
@Roger, повторяющий int[], - это тот случай, когда тогда потребуется заголовок, я имею в виду. - person Johannes Schaub - litb; 21.09.2010
comment
Резюме одной строки: поиск begin и end через ADL может не найти std::begin и std::end. - person Ben Voigt; 22.11.2010