Стандарт був змінений з моменту, коли питання (і більшість відповідей) були розміщені у вирішенні цього звіту про дефекти .
Спосіб зробити 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 / ...