Стандарт был изменен с тех пор, как вопрос (и большинство ответов) были опубликованы в решении этого отчета о дефектах .
Способ заставить 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/…