2010-01-13 8 views
6

J'ai une fonction de modèle qui fonctionne sur une variable de type modèle, et si la valeur est inférieure à 0, la met à 0. Cela fonctionne bien, mais quand mon type de modèle est non signé, je reçois un avertissement sur la façon dont la comparaison est toujours fausse. Cela a évidemment du sens, mais depuis son modèle, je voudrais qu'il soit générique pour tous les types de données (signés et non signés) et ne pas émettre l'avertissement. J'utilise g ++ sous Linux, et j'imagine qu'il existe un moyen de supprimer cet avertissement particulier via l'option de ligne de commande vers g ++, mais je voudrais quand même obtenir l'avertissement dans d'autres cas, non modélisés. . Je me demande s'il y a un moyen, dans le code, d'empêcher cela, sans avoir à écrire plusieurs versions de la fonction?La comparaison est toujours fausse en raison de la portée limitée ... avec des modèles

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

Répondre

6
#include <algorithm> 

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

Il n'est pas apparent à partir de la question que le passage par une référence non-const est approprié. Vous pouvez changer rien de retour ci-dessus (vide), passer par la valeur et le retour en termes de valeur, ou passer par const & et retour par valeur:

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

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

Generalize un peu plus, même si en dehors de la portée de votre question:

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: Eh bien, oui, c'est une solution pour ce cas particulier :) –

5

Pour le cas simple présenté, vous seriez définitivement mieux avec la solution présentée par Roger Pate.

Ce dont vous avez besoin pour la solution générale de métaprogrammation est type_traits. Vous pouvez soit utiliser ceux de boost, ou ceux fournis avec votre STL si assez moderne.

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

Prenez note cependant que is_signed est false_type pour les nombres à virgule flottante (ne demandez pas pourquoi). Pour que le code ci-dessus fonctionne avec des points flottants, vous devez typer un autre trait, par ex.

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

... et oui, plus vous obtenez dans metaprogramming le plus laid, il obtient donc ... ne pas tenir compte de cette solution et aller avec le simple énuméré par Roger: P.

+0

Vous avez manqué la partie '_impl' et l'espace de noms' detail' pour les deux premières fonctions. –

+0

Ouais remarqué :), merci –

+1

Très bonne explication et un exemple pour l'espace utilisé, et ne pas prendre mon indignation dans l'histoire d'édition trop au sérieux --- Je devais juste fixer mon nom, même si je fais parfois cette faute de frappe. * \ * siffle innocemment \ **: P –

2

le drapeau du compilateur pour supprimer l'avertissement est -Wno-type-limits pour gcc.

+0

Et '-Wno-tautologique- compare' for Clang ... – jww

1

Tous les types de -wtype_avertissement limites avertissements peuvent être supprimées au cas par cas par conversion de chaque comparaison, ce qui génère l'avertissement, en fonction factice, qui accepte les deux opérandes utilisés dans la comparaison et renvoie le résultat de comparaison des opérandes. Par exemple, trim() fonction de la question peut être convertie d'origine dans:

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

Merci de fournir une solution générique. – martinhans

Questions connexes