Диапазон, основанный на избыточности определения операторов
Глядя на n3092, в п. 6.5.5 мы находим эквивалентность для цикла, основанного на диапазоне. Затем мы продолжим говорить, что __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 начинаются (__ диапазон) и заканчиваются (__ диапазон) соответственно, где начало и конец просматриваются с зависимым от аргумента поиска (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.
Ответы
Ответ 1
Это позволяет избежать углового случая с ADL:
namespace other {
struct T {};
int begin(T*) { return 42; }
}
other::T a[3];
for (auto v : a) {}
Поскольку ADL находит другое:: begin при вызове begin(a)
, эквивалентный код прерывается, вызывая запутанную ошибку компиляции (по строкам "не может сравнивать int с другим:: T *", поскольку end(a)
возвращался T *) или другое поведение (если другой:: конец был определен и сделал что-то также неожиданное).