Стандарт був змінений з моменту, коли питання (і більшість відповідей) були розміщені у вирішенні цього звіту про дефекти .
Спосіб зробити 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&&
змінній і передають його вам як значення. Ви не можете визначити, чи повторюєтесь ви через тимчасове (або інше значення); така перевантаження не буде викликатися 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'
", коли ==
з a 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 / ...