2010-01-13 11 views
6

Tengo una función de plantilla que opera en una variable de tipo plantilla, y si el valor es menor que 0, lo pone a 0. Esto funciona bien, pero cuando mi tipo de plantilla no tiene firma, recibo una advertencia sobre cómo la comparación siempre es falsa. Obviamente, esto tiene sentido, pero desde su diseño, me gustaría que fuera genérico para todos los tipos de datos (firmados y sin firmar) y no emitir la advertencia.La comparación es siempre falsa debido al rango limitado ... con plantillas

Estoy usando g ++ en Linux, y supongo que hay una manera de suprimir esa advertencia en particular a través de la opción de línea de comando a g ++, pero aún me gustaría recibir la advertencia en otros casos sin plantilla . Me pregunto si hay alguna manera, en el código, de evitar esto, sin tener que escribir múltiples versiones de la función.

template < class T > 
T trim(T &val) 
{ 
    if (val < 0) 
    { 
    val = 0; 
    } 
    return (val); 
} 
int main() 
{ 
    char cval = 5; 
    unsigned char ucval = 5; 

    cout << "Untrimmed: " << (int)cval; 
    cval = trim(cval); 
    cout << " Trimmed: " << (int)cval << endl; 

    cout << "Untrimmed: " << (int)ucval; 
    cval = trim(ucval); 
    cout << " Trimmed: " << (int)ucval << endl; 

return (0); 
} 

Respuesta

6
#include <algorithm> 

template<class T> 
T& trim(T& val) { 
    val = std::max(T(0), val); 
    return val; 
} 

No se desprende de la pregunta que sea apropiado pasar por referencia no const. Puede cambiar la nada por encima de retorno (void), paso por valor y volver por valor, o pasar por const & y volver por valor:

template<class T> 
T trim(T const& val); 

// example use: 
value = trim(value); // likely the most clear solution 

Generalize un poco más, aunque fuera del alcance de su pregunta:

template<class T> 
T constrain(T const& value, T const& lower, T const& upper) { 
    // returns value if value within [lower, upper] (inclusive end points) 
    // returns lower if value < lower 
    // otherwise returns upper 
    assert(lower <= upper); // precondition 
    return std::min(std::max(value, lower), upper); 
} 

template<class T> 
T constrain_range(T const& value, T const& lower, T const& upper) { 
    // returns value if value within [lower, upper) (exclusive upper) 
    // returns lower if value < lower 
    // otherwise returns upper - 1 
    assert(lower < upper); // precondition 
    if  (value < lower) return lower; 
    else if (value >= upper) return upper - 1; 
    else      return value; 
} 
+0

+1: Bueno, sí, esa es una solución para este caso particular :) –

5

Por el simple caso presentado, sería definitivamente mejor con la solución presentada por Roger Pate.

Lo que necesita para la solución de metaprogramación general es type_traits. Puedes usar los de boost o los que se incluyen con tu STL si son lo suficientemente modernos.

namespace detail { 

    template < class T > 
    T trim_impl(T &val, const std::tr1::true_type&) 
    { 
    if (val < 0) 
    { 
     val = 0; 
    } 
    return (val); 
    } 

    template < class T > 
    T trim_impl(T &val, const std::tr1::false_type&) 
    { 
    return (val); 
    } 

} // end namespace detail 

template < class T > 
T trim(T &val) 
{ 
    return detail::trim_impl(val, std::tr1::is_signed<T>()); 
} 

Tomar nota sin embargo, que es is_signedfalse_type de números de punto (no pregunte por qué) flotante. Para hacer que el código anterior funcione con puntos flotantes, necesitarás ingresar otro atributo, p.

typedef std::tr1::integral_constant< bool, 
      std::tr1::is_signed<T>::value || 
      std::tr1::is_floating_point<T>::value > has_sign; 

... y sí, cuanto más profundo se meten en metaprogramming el más feo se pone tan ... no tener en cuenta esta solución e ir con el simple listado por Roger: P.

+0

se perdió la parte '' _impl' y el espacio de nombres detail' para las dos primeras funciones. –

+0

Sí notado :), gracias –

+1

Muy buena explicación y ejemplo para el espacio utilizado, y no tomo mi indignación en el historial de edición demasiado en serio --- Solo tuve que corregir mi nombre, incluso si a veces hago ese error. * \ * silba inocentemente \ **: P –

2

la bandera compilador para suprimir la advertencia es -Wno-type-limits para gcc.

+0

Y '-Wno-tautological- compare' for Clang ... – jww

1

Todos los tipos de -Wtype plazos advertencias pueden ser suprimidas caso por caso mediante la conversión de cada comparación, que genera la advertencia, en una función ficticia, que acepta ambos operandos usados ​​en la comparación y devuelve el resultado de comparación de operandos. Por ejemplo, trim() función de la pregunta original se puede convertir en:

// Dummy function for -Wtype-limits warnings suppression. 
template < class T > 
static inline dummy_less(const T &a, const T &b) 
{ 
    return (a < b); 
} 

template < class T > 
T trim(T &val) 
{ 
    if (dumy_less(val, 0)) 
    { 
    val = 0; 
    } 
    return (val); 
} 
+0

Gracias por proporcionar una solución genérica. – martinhans

Cuestiones relacionadas