GCC: la comparación siempre es verdadera debido al rango limitado de tipos de datos, ¿en el parámetro de plantilla?

Quiero escribir una plantilla que me devuelva el tipo de entero con signo más pequeño que pueda representar un número dado. Esta es mi solución:

/**
 * Helper for IntTypeThatFits.
 * Template parameters indicate whether the given number fits into 8, 16 or 32
 * bits. If neither of them is true, it is assumed that it fits 64 bits.
 */
template <bool fits8, bool fits16, bool fits32>
struct IntTypeThatFitsHelper { };

// specializations for picking the right type
// these are all valid combinations of the flags
template<> struct IntTypeThatFitsHelper<true, true, true> { typedef int8_t Result; };
template<> struct IntTypeThatFitsHelper<false, true, true> { typedef int16_t Result; };
template<> struct IntTypeThatFitsHelper<false, false, true> { typedef int32_t Result; };
template<> struct IntTypeThatFitsHelper<false, false, false> { typedef int64_t Result; };

/// Finds the smallest integer type that can represent the given number.
template <int64_t n>
struct IntTypeThatFits
{
    typedef typename IntTypeThatFitsHelper<
        (n <= numeric_limits<int8_t>::max()) && (n >= numeric_limits<int8_t>::min()), 
        (n <= numeric_limits<int16_t>::max()) && (n >= numeric_limits<int16_t>::min()), 
        (n <= numeric_limits<int32_t>::max()) && (n >= numeric_limits<int32_t>::min())
    >::Result Result;
};

Sin embargo, GCC no acepta este código. Recibo el error "la comparación siempre es verdadera debido al rango limitado del tipo de datos [-Werror=type-limits]". ¿Por qué sucede eso? n es un entero de 64 bits con signo, y todas las comparaciones pueden ser verdaderas o falsas para diferentes valores de n, ¿o estoy pasando por alto algo?

Estaré encantado de recibir cualquier ayuda.

Editar: debo mencionar que estoy usando C++ 11.

preguntado el 03 de mayo de 12 a las 16:05

¿Cómo podría ser algo no menos que max y no más que min? ¿Estoy leyendo eso mal? -

@SethCarnegie: Está comprobando si el n está dentro de los rangos de una experiencia diferente tipos de datos. -

Para aquellos que quieran intentarlo, logré obtener una versión en idea (que no es compatible constexpr aún...) -

5 Respuestas

Es un problema con gcc, las advertencias en el código con plantilla pueden ser frustrantes. Puede cambiar la advertencia o utilizar otro enfoque.

Como sabrá, el código con plantilla se analiza dos veces:

  • una vez cuando se encontró por primera vez (análisis)
  • una vez cuando se instancia para un tipo/valor dado

El problema aquí es que en la instanciación, la verificación es trivial (sí 65 encaja en un int gracias), y el compilador no se da cuenta de que esta advertencia no es válida para todas las instancias :( De hecho, es muy frustrante para aquellos de nosotros que nos esforzamos por tener una experiencia de compilación sin advertencias con advertencias on.

Tienes 3 posibilidades:

  • desactivar esta advertencia, o degradarla a un no error
  • use un pragma para desactivarlo selectivamente para este código
  • reelabore el código en otro formato para que ya no active la advertencia

Tenga en cuenta que a veces la tercera posibilidad implica un cambio masivo y una solución mucho más complicada. Aconsejo contra el código complicado. a unos para deshacerse de las advertencias despistadas.

EDITAR:

Una posible solución:

template <int64_t n>
struct IntTypeThatFits {
    static int64_t const max8 = std::numeric_limits<int8_t>::max();
    static int64_t const min8 = std::numeric_limits<int8_t>::min();

    static int64_t const max16 = std::numeric_limits<int16_t>::max();
    static int64_t const min16 = std::numeric_limits<int16_t>::min();

    static int64_t const max32 = std::numeric_limits<int32_t>::max();
    static int64_t const min32 = std::numeric_limits<int32_t>::min();

    typedef typename IntTypeThatFitsHelper<
        (n <= max8 ) && (n >= min8 ), 
        (n <= max16) && (n >= min16), 
        (n <= max32) && (n >= min32)
    >::Result Result;
};

... al cambiar el tipo de datos utilizados en la comparación, debería silenciar la advertencia del compilador. Supongo que casting explícito (int64_t(std::numeric_limits<int8_t>::max())) también podría funcionar, pero encontré esto más legible.

contestado el 03 de mayo de 12 a las 17:05

Entonces, ¿esto es un error en GCC? Después de todo, la advertencia solo tiene sentido para las comparaciones de tiempo de ejecución. - benjamin schug

@BenjaminSchug: No lo calificaría como un error porque se podría argumentar que funciona como se esperaba. Después de todo, en algunos casos podrías quieres para ser advertido en el momento de la instanciación :x Es solo que en tu caso es dañino... - Matthieu M.

En este contexto, la comparación es son requeridos para ser una constante de tiempo de compilación, de lo contrario no podría usarlo como argumento de plantilla. Entonces, si este es realmente el motivo de la advertencia, ¿cómo es que no es un error? - benjamin schug

@BenjaminSchug: publiqué una solución al problema que experimentó. La idea es utilizar el valor proporcionado por numeric_limit<X>::min et al. pero en un tipo que es más aceptable para los análisis de gcc. - Matthieu M.

@BenjaminSchug: En este contexto preciso, estoy de acuerdo en que probablemente podría eliminarse, pero es un error solo si las especificaciones dicen que debería comportarse de otra manera;) Dicho esto, dado que gcc está abierto a discusión, tal vez podría señalarlo en su lista de correo , podrían estar interesados ​​en este problema. - Matthieu M.

El error ocurre porque le pidió a GCC que le diera errores sobre esta advertencia con -Werror=type-limits. La advertencia -Wtype-limits le da una advertencia si alguna vez hace una comparación que siempre será cierta debido a los rangos de los tipos de datos dados, por ejemplo:

uint8_t x;
if(x >= 0) { ... }  // always true, unsigned integers are non-negative
if(x >= 256) { ... }  // always false

int32_t x;
if(x < 9223372036854775808LL) { ... }  // always true

Esta advertencia a veces puede ser útil, pero en muchos casos incluir esto es solo una pedantería inútil y puede ignorarse. Normalmente es una advertencia (habilitada como parte de -Wextra, si usas eso), pero con -Werror or -Werror=type-limits, GCC lo convierte en un error.

Dado que en este caso no es realmente indicativo de un problema potencial con su código, simplemente apague la advertencia con -Wno-type-limits, o hacer que no sea un error con Werror=no-type-limits si no le importa ver esas advertencias en la salida del compilador.

contestado el 03 de mayo de 12 a las 16:05

¿En qué parte del código del OP hay una comparación que siempre es cierta debido al rango de tipos de datos? - interjay

No quiero desactivar esa advertencia por completo porque es útil en el caso normal. Aquí simplemente no debería activarse porque esa comparación es Supuesto y son requeridos ser una constante de tiempo de compilación. - benjamin schug

   typedef typename IntTypeThatFitsHelper<
        (n <= numeric_limits<int8_t>::max()) && (n >= numeric_limits<int8_t>::min()), 
        (n <= numeric_limits<int16_t>::max()) && (n >= numeric_limits<int16_t>::min()), 
        (n <= numeric_limits<int32_t>::max()) && (n >= numeric_limits<int32_t>::min())
    >::Result Result;

No puedes hacer eso en C++ (en C++11 puedes) - numeric_limits<int8_t>::max() no es una constante de tiempo de compilación. ¿Estás usando C++ 11?

Por cierto, Boost ya te proporciona esto: http://www.boost.org/doc/libs/1_49_0/libs/integer/doc/html/boost_integer/integer.html

contestado el 03 de mayo de 12 a las 16:05

Sí, estoy usando C++ 11. Debería haber mencionado eso, lo siento. - benjamin schug

Gracias por el consejo con Boost, eso parece funcionar. Realmente siempre debería revisar Boost antes de escribir algo yo mismo. :-)- benjamin schug

Creo que las otras respuestas de cuál es el problema son incorrectas. No creo que este sea un caso de un compilador demasiado ansioso, pero creo que es un error del compilador. Este código todavía dispara la advertencia:

template<int64_t n>
bool a() {
    return (n <= static_cast<int64_t>(std::numeric_limits<int8_t>::max()));
}

Al llamar a<500>();, pero este código no:

template<int64_t n>
bool a() {
    return (n <= static_cast<int64_t>(127));
}

std::numeric_limits::max() se evalúa en 127. Presentaré un informe de bugzilla para esto más tarde hoy si nadie más lo hace.

contestado el 03 de mayo de 12 a las 17:05

Podría considerar un compilador demasiado ansioso como un compilador con errores :-) - Gunther piez

Acabo de enviar un informe de error a su lista de correo: gcc.gnu.org/ml/gcc-bugs/2012-05/msg00311.html - benjamin schug

@BenjaminSchug Mejor usa bugzilla la próxima vez gcc.gnu.org/bugzilla . Todo lo que se informe allí se envía a la lista de correo de todos modos. - Gunther piez

Recibe la advertencia porque para algunas instancias de template <int64_t n> struct IntTypeThatFits con pequeño n (más pequeño que 2 ^ 32) algunas de las comparaciones siempre son verdaderas (¡sic!) Debido al rango del operando durante el tiempo de compilación .

Esto podría considerarse una advertencia falsa en este caso, porque su código se basa en él, OTOH solicitó explícitamente convertirlo en un error con un -Werror o un interruptor de línea de comando similar, básicamente obtienes lo que pediste aquí.

contestado el 03 de mayo de 12 a las 16:05

básicamente obtienes lo que pediste aquí -> No estoy de acuerdo. Es una advertencia útil en muchas situaciones, y hay muchas instancias de la plantilla en las que no se activará. Desearía que los compiladores evitaran tales advertencias cuando no son válidas para todas las instancias... aunque reconozco que es complicado de evaluar. - Matthieu M.

En mi humilde opinión generalmente usando -Werror solo busca problemas. Las advertencias son algo bueno, pero hay muchas advertencias falsas en gcc por ahí para convertirlas en errores todo el tiempo. Especialmente con código con plantilla evaluado durante el tiempo de compilación. - Gunther piez

No tengo problemas con -Werror en mi código, y así es como se ve mi lista de advertencias: stackoverflow.com/questions/5088460/… (Ocasionalmente enciendo esas otras advertencias que menciono pero digo que no uso). - David Stone

@drhirsch: Teoría de la ventana rota => tan pronto como deja escapar las advertencias en la compilación, se propagan, hasta las pocas advertencias útiles (-Wreturn !!) se pierden en el ruido. Prefiero tener una lista de advertencias estrecha y cuidadosamente seleccionada, y tenerlas en errores, en lugar de montones de advertencias inútiles que aparecen en la pantalla o ninguna advertencia en absoluto. - Matthieu M.

@DavidStone Esto probablemente esté estrechamente relacionado con el estilo de codificación. Para mí, -Wunused siempre está activado, definir una variable para su uso posterior y no usarla es un gran no :-) OTOH -pedantic simplemente no funcionará para mí. - Gunther piez

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.