Closed Loop

How can you fix this code?

template <typename T> void closed_range(T begin, T end) { for (T i = begin; i <= end; ++i) { // do something } } 
  • T is limited to an integer type, may be the broader of such types, and may be signed or unsigned

  • begin may be numeric_limits<T>::min()

  • end can be numeric_limits<T>::max() (in this case ++i will overflow in the above code)

I have several ways, but I don't like it.

+4
source share
5 answers

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 { // do something } while (first != last && advance(first)); } } 

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.

+6
source

My welcome:

 // Make sure we have at least one iteration if (begin <= end) { for (T i = begin; ; ++i) { // do something // Check at the end *before* incrementing so this won't // be affected by overflow if (i == end) break; } } 
+5
source

This works and is pretty clear:

 T i = begin; do { ... } while (i++ < end); 

If you want to catch the special case begin >= end , you need to add another if , as in Steve Jessop's solution.

+4
source
 template <typename T, typename F> void closed_range(T begin, T end, F functionToPerform) { for (T i = begin; i != end; ++i) { functionToPerform(i); } functionToPerform(end); } 
+3
source

EDIT: Recycled things to more closely match the OP.

 #include <iostream> using namespace std; template<typename T> void closed_range(T begin, T end) { for( bool cont = (begin <= end); cont; ) { // do something cout << begin << ", "; if( begin == end ) cont = false; else ++begin; } // test - this should return the last element cout << " -- " << begin; } int main() { closed_range(10, 20); return 0; } 

Conclusion:

10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 20

0
source

Source: https://habr.com/ru/post/1304303/


All Articles