Может быть,
template <typename T> void closed_range(T begin, const T end)
if (begin <= end) {
do {
// do something
} while (begin != end && (++begin, true));
}
}
Проклятия, моя первая попытка была неудачной, и исправление выше не так красиво, как я надеялся. Как насчет:
template <typename T> bool advance(T &value) { ++value; return true; }
template <typename T> void closed_range(T first, const T last)
if (first <= last) {
do {
// do something
} while (first != last && advance(first));
}
}
Нет никакой двусмысленности с std::advance
, даже если T не является целочисленным типом, поскольку std::advance
принимает 2 параметра. Таким образом, шаблон также будет работать, например, с итератором с произвольным доступом, если по какой-то причине вы хотели бы иметь закрытый диапазон из них.
Или как насчет теории множеств? Очевидно, что это является огромным излишним, если вы пишете только один цикл в закрытом диапазоне, но если это то, что вы хотите сделать много, то это делает код цикла правильным. Не уверен насчет эффективности: в очень узком цикле вы можете убедиться, что вызов endof
поднят:
#include <limits>
#include <iostream>
template <typename T>
struct omega {
T val;
bool isInfinite;
operator T() { return val; }
explicit omega(const T &v) : val(v), isInfinite(false) { }
omega &operator++() {
(val == std::numeric_limits<T>::max()) ? isInfinite = true : ++val;
return *this;
}
};
template <typename T>
bool operator==(const omega<T> &lhs, const omega<T> &rhs) {
if (lhs.isInfinite) return rhs.isInfinite;
return (!rhs.isInfinite) && lhs.val == rhs.val;
}
template <typename T>
bool operator!=(const omega<T> &lhs, const omega<T> &rhs) {
return !(lhs == rhs);
}
template <typename T>
omega<T> endof(T val) {
omega<T> e(val);
return ++e;
}
template <typename T>
void closed_range(T first, T last) {
for (omega<T> i(first); i != endof(last); ++i) {
// do something
std::cout << i << "\n";
}
}
int main() {
closed_range((short)32765, std::numeric_limits<short>::max());
closed_range((unsigned short)65533, std::numeric_limits<unsigned short>::max());
closed_range(1, 0);
}
Выход:
32765
32766
32767
65533
65534
65535
Будьте осторожны, используя другие операторы для omega<T>
объектов. Я реализовал только абсолютный минимум для демонстрации, и omega<T>
неявно преобразуется в T
, так что вы обнаружите, что можете писать выражения, которые потенциально отбрасывают «бесконечность» объектов омега. Вы можете исправить это, объявив (не обязательно определяя) полный набор арифметических операторов; или с помощью исключения в преобразовании, если isInfinite имеет значение true; или просто не беспокойтесь об этом на том основании, что вы не можете случайно преобразовать результат обратно в омегу, потому что конструктор явный. Но, например, omega<int>(2) < endof(2)
- это правда, а omega<int>(INT_MAX) < endof(INT_MAX)
- это ложь.