Стандарт был изменен с тех пор, как вопрос (и большинство ответов) были опубликованы в решении этого отчета о дефектах .
Способ заставить for(:)цикл работать с вашим типом Xтеперь один из двух:
Создайте член X::begin()и X::end()верните что-то, что действует как итератор
Создать бесплатную функцию begin(X&)и end(X&)что возвратное то , что действует как итератор, в том же пространстве имен вашего типа X.¹
И похоже на constвариации. Это будет работать как на компиляторах, которые реализуют изменения отчета о дефектах, так и на компиляторах, которые этого не делают.
Возвращаемые объекты не обязательно должны быть итераторами. for(:)Петля, в отличие от большей части C ++ стандарта, как указана для расширения к чему - то , эквивалентному :
for( range_declaration : range_expression )
будет выглядеть так:
{
auto && __range = range_expression ;
for (auto __begin = begin_expr,
__end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
где переменные, начинающиеся с __, предназначены только для экспозиции, begin_exprи end_exprэто волшебство, которое вызывает begin/ end.²
Требования к возвращаемому значению начала / конца просты: вы должны предварительно перегрузить ++, обеспечить правильность выражений инициализации, двоичный файл, !=который можно использовать в логическом контексте, унарный код , *который возвращает то, что вы можете назначить-инициализировать range_declaration, и выставить открытый деструктор.
Делать это способом, несовместимым с итератором, вероятно, плохая идея, так как будущие итерации C ++ могут быть относительно коварными в нарушении вашего кода, если вы это сделаете.
Кроме того, вполне вероятно, что в будущем пересмотр стандарта позволит end_exprвернуть другой тип, чем begin_expr. Это полезно в том смысле, что оно допускает оценку «ленивого конца» (например, обнаружение нулевого завершения), которую легко оптимизировать, чтобы она была такой же эффективной, как рукописный цикл C, и другие подобные преимущества.
¹ Обратите внимание, что for(:)циклы хранят любой временный объект в auto&&переменной и передают его вам как lvalue. Вы не можете определить, выполняете ли вы итерацию по временному (или другому значению); такая перегрузка не будет вызвана for(:)циклом. См. [Stmt.ranged] 1.2-1.3 из n4527.
² Либо вызовите метод begin/ end, либо поиск свободной функции begin/ только для ADL end, либо используйте магию для поддержки массивов в стиле C. Обратите внимание, что std::beginне вызывается, если не range_expressionвозвращает объект типа в namespace stdили зависит от него.
В C ++ 17 выражение для диапазона было обновлено
{
auto && __range = range_expression ;
auto __begin = begin_expr;
auto __end = end_expr;
for (;__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
с типами __beginи __endбыли отделены.
Это позволяет конечному итератору не совпадать с типом начала. Типом конечного итератора может быть «часовой», который поддерживается только !=с типом начального итератора.
Практическим примером того, почему это полезно, является то, что ваш конечный итератор может читать «проверьте ваш, char*чтобы увидеть, если он указывает '0'», когда ==с char*. Это позволяет C ++ диапазонному выражению генерировать оптимальный код при итерации по нулевому завершающему char*буферу.
struct null_sentinal_t {
template<class Rhs,
std::enable_if_t<!std::is_same<Rhs, null_sentinal_t>{},int> =0
>
friend bool operator==(Rhs const& ptr, null_sentinal_t) {
return !*ptr;
}
template<class Rhs,
std::enable_if_t<!std::is_same<Rhs, null_sentinal_t>{},int> =0
>
friend bool operator!=(Rhs const& ptr, null_sentinal_t) {
return !(ptr==null_sentinal_t{});
}
template<class Lhs,
std::enable_if_t<!std::is_same<Lhs, null_sentinal_t>{},int> =0
>
friend bool operator==(null_sentinal_t, Lhs const& ptr) {
return !*ptr;
}
template<class Lhs,
std::enable_if_t<!std::is_same<Lhs, null_sentinal_t>{},int> =0
>
friend bool operator!=(null_sentinal_t, Lhs const& ptr) {
return !(null_sentinal_t{}==ptr);
}
friend bool operator==(null_sentinal_t, null_sentinal_t) {
return true;
}
friend bool operator!=(null_sentinal_t, null_sentinal_t) {
return false;
}
};
живой пример в компиляторе без полной поддержки C ++ 17; forцикл вручную расширен.
begin/endили друга, статического или свободногоbegin/end. Просто будьте осторожны, в какое пространство имен вы помещаете бесплатную функцию: stackoverflow.com/questions/28242073/…