Pregunta

¿Cómo solucionar este código?

template <typename T> void closed_range(T begin, T end)
{
    for (T i = begin; i <= end; ++i) {
        // do something
    }
}
  • T está restringido a ser un tipo entero, puede ser el más amplio de tales tipos y pueden ser con o sin signo

  • begin puede numeric_limits<T>::min()

  • end puede numeric_limits<T>::max() (en cuyo caso ++i se desbordará en el código anterior)

Tengo varias maneras, pero ninguna me gusta mucho.

¿Fue útil?

Solución

Tal vez,

template <typename T> void closed_range(T begin, const T end)
    if (begin <= end) {
        do {
            // do something
        } while (begin != end && (++begin, true));
    }
}

Las maldiciones, mi primer intento fue mal, y la revisión anterior no es tan bonita como yo esperaba. ¿Qué hay de:

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));
    }
}

No hay ambigüedad con std::advance incluso si T no es un tipo entero, ya que std::advance toma 2 parámetros. Por lo que la plantilla también trabajaría con, por ejemplo, un iterador de acceso aleatorio, si por alguna razón quería un rango cerrado de ellos.

O qué tal un poco de la teoría de conjuntos? Obviamente, esto es un exceso masivo si sólo está escribiendo un bucle en un rango cerrado, pero si es algo que usted quiere hacer mucho, entonces tiene el código de bucle sobre la derecha. No está seguro acerca de la eficiencia: en un bucle muy apretado es posible que desee asegurarse de que la llamada a endof se iza:

#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);
}

Salida:

32765
32766
32767
65533
65534
65535

ser un poco cuidadoso uso de otros operadores en objetos omega<T>. Sólo he implementado el mínimo absoluto para la demostración, y omega<T> conversos implícitamente a T, por lo que encontrará que se puede escribir expresiones que potencialmente tirar el "infinito" de objetos omega. Se podría arreglar eso declarando (no necesariamente definir) un conjunto completo de operadores aritméticos; o por lanzar una excepción en la conversión si isInfinite es cierto; o simplemente no se preocupe por motivos que no se puede convertir accidentalmente el resultado de nuevo a un omega, porque el constructor es explícito. Pero por ejemplo, omega<int>(2) < endof(2) es cierto, pero omega<int>(INT_MAX) < endof(INT_MAX) es falso.

Otros consejos

Mi opinión:

// 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;
    }
}

Esto funciona y es bastante claro:

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

Si se desea capturar el caso especial de begin >= end es necesario agregar otra if como en la solución de Steve Jessop.

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);
} 

Edit: Reworked cosas que se asemejan más a la 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;
}

La salida es:

  

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top