Рассмотрим спецификацию диапазона для цикла begin-expr и end-expr (N4140 [stmt.ranged]/p1). Учитывая диапазон __range типа _RangeT,
begin-expr и end-expr определяются следующим образом:
- if
_RangeT- тип массива, begin-expr и end-expr -__rangeи__range + __bound, соответственно, где__boundравно связанный массив. Если_RangeT- массив неизвестного размера или массив неполного типа, программа плохо сформирована;- Если
_RangeT- это тип класса, то неквалифицированные-идентификаторыbeginиendпросматриваются в области класса_RangeT, как если бы доступ к члену класса поиск (3.4.5), и если либо (или оба) найдут хотя бы один объявления, begin-expr и end-expr являются__range.begin()и__range.end(), соответственно;- в противном случае begin-expr и end-expr равны
begin(__range)иend(__range)соответственно, гдеbeginиendпросматриваются в связанные пространства имен (3.4.2). [Примечание: Обычный неквалифицированный поиск (3.4.1) не выполняется. -end note]
Можно ли смоделировать это точное поведение в обычном С++-коде? т.е. можно написать шаблон функции magic_begin и a magic_end, так что
for(auto&& p : range_init) { /* statements */ }
и
{
auto&& my_range = range_init;
for(auto b = magic_begin(my_range), e = magic_end(my_range); b != e; ++b){
auto&& p = *b;
/* statements */
}
}
всегда имеют то же самое поведение?
Неответы включают в себя квалифицированные вызовы на std::begin/std::end (не обрабатывает третью пулю, между прочим) и using std::begin; begin(range);, потому что, между прочим, это неоднозначно, если ADL для begin находит перегрузка, которая одинаково хороша как std::begin.
Для иллюстрации, данный
namespace foo {
struct A { int begin; };
struct B { using end = int; };
class C { int* begin(); int *end(); }; // inaccessible
struct D { int* begin(int); int* end();};
struct E {};
template<class T> int* begin(T&) { return nullptr; }
template<class T> int* end(T&) { return nullptr; }
}
foo::A a; foo::B b; foo::C c; foo::D d; foo::E e;
Я хочу magic_begin(a)/magic_begin(b)/magic_begin(c)/magic_begin(d) быть компиляционной ошибкой и magic_begin(e) для возврата (int*)nullptr.