2011-01-21 17 views
18

dado:especialización de plantilla parcial basada en "firm-ness" de tipo entero?

template<typename T> 
inline bool f(T n) { 
    return n >= 0 && n <= 100; 
} 

Cuando se utiliza con un tipo de unsigned genera una advertencia:

unsigned n; 
f(n); // warning: comparison n >= 0 is always true 

¿Hay alguna manera inteligente para no hacer la comparación n >= 0 cuando T es un tipo de unsigned? He intentado añadir una especialización de plantilla parcial:

template<typename T> 
inline bool f(unsigned T n) { 
    return n <= 100; 
} 

pero gcc 4.2.1 no le gusta eso. (No creo que tipo de especialización de plantilla parcial sería legal de todos modos.)

+2

Tenga en cuenta que no hay una especialización parcial para plantillas de función única, la especialización completa. Dicho esto, la especialización completa suele ser una mala idea para las plantillas de funciones porque las reglas sobre lo que se especializa, lo que se sobrecarga y cómo la resolución de sobrecarga decide qué usar son complicadas y complicadas. Afortunadamente, la sobrecarga y SFINAE (falla de sustitución no es un error) son suficientes aquí. –

+0

No recibo ninguna advertencia de Clang 3.8 (o GCC 8.0) sobre esto. Si elimino la plantilla en 'f', lo entiendo. ¿Hay una versión de '-Wtautological-compare' que considere instancias de plantillas? – user2023370

Respuesta

22

Puede utilizar enable_if con el rasgo is_unsigned Tipo:

template <typename T> 
typename std::enable_if<std::is_unsigned<T>::value, bool>::type f(T n) 
{ 
    return n <= 100; 
} 

template <typename T> 
typename std::enable_if<!std::is_unsigned<T>::value, bool>::type f(T n) 
{ 
    return n >= 0 && n <= 100; 
} 

puede encontrar enable_if y is_unsigned en los espacios de nombres std o std::tr1 si su compilador soporta C++ 0x o TR1, respectivamente. De lo contrario, Boost tiene una implementación de la biblioteca de rasgos de tipo, Boost.TypeTraits. La implementación de impulso de enable_if es un poco diferente; boost::enable_if_c es similar al TR1 y C++ 0x enable_if.

14

Puede aprovechar el comportamiento de envolvente de enteros sin signo.

template<bool> struct bool_ { }; 

template<typename T> 
inline bool f(T n, bool_<false>) { 
    return n >= 0 && n <= 100; 
} 

template<typename T> 
inline bool f(T n, bool_<true>) { 
    return n <= 100; 
} 

template<typename T> 
inline bool f(T n) { 
    return f(n, bool_<(static_cast<T>(-1) > 0)>()); 
} 

Es importante no especificar >= 0, para evitar una advertencia de nuevo. A continuación aparece para engañar a GCC también

template<typename T> 
inline bool f(T n) { 
    return (n == 0 || n > 0) && n <= 100; 
} 
+0

+1 para ese último truco – TonyK

0

puede implementar una aplicación especial función de plantilla para unsigned tipo como:

template<class T> bool f(T val); 
template<> bool f<unsigned>(unsigned val); 

ACTUALIZACIÓN bandera sin signo

Puede aplicar diferentes implementaciones para todos tipos sin firmar que desea usar o agregue una bandera bool como:

template <class T, bool U> bool f(T val) 
{ 
     if (U) 
       return val <= 100; 
     else 
       return (val >=0) && (val <= 100); 
} 

... 

cout << f<int, false>(1) << endl; 
cout << f<int, false>(-1) << endl; 
cout << f<char, true>(10) << endl; 
+0

Excepto que tiene que funcionar para todos los tipos sin firmar: char, short, int, long. –

+0

Su solución es mucho más detallada que las otras que aprovechan "enable_if" (o una funcionalidad similar). –

+1

Su solución también parece ser la única que se puede implementar en C++ 03, sin utilizar boost. Esa es una ventaja clara en mi libro. Volveré a votar cuando lo ponga a funcionar;) – Paul

1

¿Hay alguna manera inteligente de no hacer la comparación n> = 0 cuando T es un tipo sin signo? Traté de agregar una especialización de plantilla parcial:

El optimizador debería eliminar el código para la comparación ya que detectó la condición.

Para Clang, agregue -Wno-tautological-compare para desactivar la advertencia. Para GCC/G ++, agregue -Wno-type-limits para aplastar la advertencia.

Si está utilizando un compilador que admite pragma diagnostic {push|pop} puede:

#if (GCC_VERSION >= 40600) || (LLVM_CLANG_VERSION >= 10700) || (APPLE_CLANG_VERSION >= 20000) 
# define GCC_DIAGNOSTIC_AVAILABLE 1 
#endif  

#if MSC_VERSION 
# pragma warning(push) 
# pragma warning(disable: 4389) 
#endif 

#if GCC_DIAGNOSTIC_AVAILABLE 
# pragma GCC diagnostic push 
# pragma GCC diagnostic ignored "-Wsign-compare" 
# if (LLVM_CLANG_VERSION >= 20800) || (APPLE_CLANG_VERSION >= 30000) 
# pragma GCC diagnostic ignored "-Wtautological-compare" 
# elif (GCC_VERSION >= 40300) 
# pragma GCC diagnostic ignored "-Wtype-limits" 
# endif 
#endif 

template<typename T> 
inline bool f(T n) { 
    return n >= 0 && n <= 100; 
} 

#if GCC_DIAGNOSTIC_AVAILABLE 
# pragma GCC diagnostic pop 
#endif 

#if MSC_VERSION 
# pragma warning(pop) 
#endif 

ver también Comparison is always false due to limited range…

Cuestiones relacionadas