2010-01-13 4 views
6

Ho una funzione basata su modello che opera su una variabile di tipo template e se il valore è minore di 0, lo imposta su 0. Funziona bene, ma quando il mio modello di tipo non è firmato, ricevo un avviso su come il confronto è sempre falso. Questo ovviamente ha senso, ma dal momento che il suo modello, mi piacerebbe che fosse generico per tutti i tipi di dati (firmati e non firmati) e non emettere l'avviso.Il confronto è sempre falso a causa della portata limitata ... con i modelli

Sto usando g ++ su Linux, e sto indovinando che c'è un modo per sopprimere quel particolare avvertimento tramite l'opzione da linea di comando a g ++, ma mi piacerebbe comunque ricevere l'avviso in altri casi, senza modelli . Mi chiedo se c'è del modo, nel codice, per evitare ciò, senza dover scrivere più versioni della funzione?

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

risposta

6
#include <algorithm> 

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

Non è chiaro dalla domanda che il passaggio tramite riferimento non const sia appropriato. È possibile modificare il nulla al di sopra di ritorno (void), passaggio per valore e tornare per valore, o passare const & e rientro per valore:

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

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

Generalize un po 'di più, anche se al di fuori della portata della tua domanda:

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: Beh, sì, questa è una soluzione per questo caso particolare :) –

5

Per il semplice caso presentato, si sarebbe definitivamente meglio con la soluzione presentata da Roger Pate.

Quello che serve per la soluzione di metaprogrammazione generale è type_traits. Puoi utilizzare quelli di boost o quelli forniti con il tuo STL se abbastanza moderno.

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

Prendete nota, tuttavia, che è is_signedfalse_type per i numeri in virgola (non chiedere perché) galleggiante. Per fare in modo che il codice precedente funzioni con i punti in virgola mobile, devi digitare un altro tratto, ad es.

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

... e sì, il più profondo lo entrano in metaprogrammazione il più brutto diventa così ... ignorare questa soluzione e andare con la semplice quello elencato da Roger: P.

+0

vi siete persi la parte '_impl' e il' namespace detail' per le prime due funzioni. –

+0

Sì, ho notato :), grazie –

+1

Ottima spiegazione ed esempio dello spazio utilizzato, e non prendere troppo sul serio il mio oltraggio nella cronologia delle modifiche --- Dovevo solo aggiustare il mio nome, anche se a volte eseguo quel refuso. * \ * fischia innocentemente \ **: P –

2

la bandiera compilatore per eliminare l'avviso è -Wno-type-limits per gcc.

+0

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

1

Tutti i tipi di -Wtype termini avvisi possono essere soppressi caso per caso convertendo ogni confronto, che genera l'avviso, in una funzione fittizia, che accetta due operandi utilizzati nel confronto e restituisce il risultato confronto degli operandi. Per esempio, assetto() funzione dalla domanda iniziale può essere convertito in:

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

Grazie per aver fornito una soluzione generica. – martinhans