May be,
template <typename T> void closed_range(T begin, const T end) if (begin <= end) { do { // do something } while (begin != end && (++begin, true)); } }
Damn, my first attempt was wrong, and the correction above is not as beautiful as I had hoped. What about:
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 {
There is no ambiguity with std::advance , even if T is not an integer type, since std::advance takes 2 parameters. Thus, the template will also work, for example, using a random access iterator, if for some reason you need a private range.
Or what about set theory? Obviously, this is a massive excess, if you write only one loop over a closed range, but if this is what you want to do a lot, then this makes the loop code correct. Not sure about efficiency: in the toughest loop, you can make sure that the endof call endof raised:
#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); }
Conclusion:
32765 32766 32767 65533 65534 65535
Be careful when using other operators on omega<T> objects. I just did the absolute minimum for the demonstration, and omega<T> implicitly converted to T , so you will find that you can write expressions that could potentially throw away the "infinity" of omega objects. You can fix this by declaring (not necessarily defining) a complete set of arithmetic operators; or by throwing an exception in the transformation if isInfinite is true; or just donβt worry about it on the grounds that you cannot accidentally convert the result back to omega because the constructor is explicit. But, for example, omega<int>(2) < endof(2) is true, but omega<int>(INT_MAX) < endof(INT_MAX) is false.