Maybe,
template <typename T> void closed_range(T begin, const T end)
if (begin <= end) {
do {
// do something
} while (begin != end && (++begin, true));
}
}
Curses, my first attempt was wrong, and the fix above isn't as pretty as I'd hoped. How 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 {
// do something
} while (first != last && advance(first));
}
}
There's no ambiguity with std::advance
even if T isn't an integer type, since std::advance
takes 2 parameters. So the template would also work with for instance a random-access iterator, if for some reason you wanted a closed range of those.
Or how about a bit of set theory? Obviously this is massive overkill if you're only writing one loop over a closed range, but if it's something that you want to do a lot, then it makes the loop code about right. Not sure about efficiency: in a really tight loop you might want make sure the call to endof
is hoisted:
#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);
}
Output:
32765
32766
32767
65533
65534
65535
Be a bit careful using other operators on omega<T>
objects. I've only implemented the absolute minimum for the demonstration, and omega<T>
implicitly converts to T
, so you'll find that you can write expressions which potentially throw away the "infiniteness" of omega objects. You could fix that by declaring (not necessarily defining) a full set of arithmetic operators; or by throwing an exception in the conversion if isInfinite is true; or just don't worry about it on grounds that you can't accidentally convert the result back to an 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.