Peut-être,
template <typename T> void closed_range(T begin, const T end)
if (begin <= end) {
do {
// do something
} while (begin != end && (++begin, true));
}
}
Curses, ma première tentative a eu tort, et le correctif ci-dessus ne sont pas aussi jolie que je l'avais espéré. Que diriez-vous:
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));
}
}
Il n'y a pas d'ambiguïté std::advance
même si T est pas un type entier, depuis std::advance
prend 2 paramètres. Ainsi, le modèle fonctionnerait également avec, par exemple, un itérateur à accès aléatoire, si pour une raison quelconque vous vouliez une plage fermée de ceux-ci.
Ou que diriez-vous d'un peu de théorie des ensembles? Évidemment, il s'agit d'une surenchère massive si vous n'écrivez qu'une seule boucle sur une plage fermée, mais si c'est quelque chose que vous voulez faire beaucoup, alors cela rend le code de boucle à peu près correct. Vous ne savez pas sur l'efficacité: dans une boucle vraiment serré, vous voudrez peut-être vous assurer que l'appel à endof
est hissée:
#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);
}
Sortie:
32765
32766
32767
65533
65534
65535
être prudent en utilisant d'autres opérateurs sur omega<T>
objets. J'ai seulement implémenté le minimum absolu pour la démonstration, et omega<T>
convertit implicitement en T
, donc vous constaterez que vous pouvez écrire des expressions qui peuvent potentiellement jeter l'infinité des objets oméga. Vous pouvez corriger cela en déclarant (sans nécessairement définir) un ensemble complet d'opérateurs arithmétiques; ou en lançant une exception dans la conversion si isInfinite est true; ou tout simplement ne vous inquiétez pas à cause du fait que vous ne pouvez pas accidentellement convertir le résultat en oméga, car le constructeur est explicite. Mais par exemple, omega<int>(2) < endof(2)
est vrai, mais omega<int>(INT_MAX) < endof(INT_MAX)
est faux.
@ralu: tout le point de la question est que c'est <=, pas <. C'est la différence entre une gamme fermée et une gamme semi-ouverte. –
Question intéressante en effet. J'ai eu les mêmes problèmes dans les boucles décroissantes >> 'pour (size_t i = 10; i> 0; - i)' n'a pas de sens puisque '0' est la valeur minimale que' size_t' peut atteindre (étant non signé). –