2010-03-29 18 views
7

consideri il seguente esempio:Come posso scrivere un modello di funzione per tutti i tipi con un particolare tipo di tratto?

struct Scanner 
{ 
    template <typename T> 
    T get(); 
}; 

template <> 
string Scanner::get() 
{ 
    return string("string"); 
} 

template <> 
int Scanner::get() 
{ 
    return 10; 
} 

int main() 
{ 
    Scanner scanner; 
    string s = scanner.get<string>(); 
    int i = scanner.get<int>(); 
} 

La classe Scanner viene utilizzato per estrarre i token da qualche fonte. Il codice precedente funziona correttamente, ma non riesce quando provo a get altri tipi integrali come uno char o uno unsigned int. Il codice per leggere questi tipi è esattamente lo stesso del codice per leggere un int. Potrei semplicemente duplicare il codice per tutti gli altri tipi interi che vorrei leggere, ma preferirei definire un modello di funzione per tutti i tipi interi.

ho provato la seguente:

struct Scanner 
{ 
    template <typename T> 
    typename enable_if<boost::is_integral<T>, T>::type get(); 
}; 

che funziona come un fascino, ma non sono sicuro come ottenere Scanner::get<string>() a funzionare di nuovo. Quindi, come posso scrivere il codice in modo che possa fare scanner.get<string>() e scanner.get<any integral type>() e avere una singola definizione per leggere tutti i tipi interi?

Aggiornamento: domanda bonus: Cosa succede se desidero accettare più di una gamma di classi basate su alcuni tratti? Ad esempio: come dovrei affrontare questo problema se voglio avere tre funzioni get che accettano (i) tipi interi (ii) tipi a virgola mobile (iii) stringhe, rispettivamente.

risposta

10
struct Scanner 
{ 
    template <typename T> 
    typename boost::enable_if<boost::is_integral<T>, T>::type get() 
    { 
     return 10; 
    } 
    template <typename T> 
    typename boost::disable_if<boost::is_integral<T>, std::string>::type get() 
    { 
     return "string"; 
    } 
}; 

Aggiornamento

struct Scanner 
{ 
    template <typename T> 
    typename boost::enable_if<boost::is_integral<T>, T>::type get() 
    { 
     return 10; 
    } 

    template <typename T> 
    typename boost::enable_if<boost::is_floating_point<T>, T>::type get() 
    { 
     return 11.5; 
    } 

    template <typename T> 
    std::string get(
      typename boost::disable_if<boost::is_floating_point<T>, T>::type* = 0, 
      typename boost::disable_if<boost::is_integral<T>, T>::type* = 0) 

    { 
     return std::string("string"); 
    } 
}; 
+2

Vorrei sottolineare che potreste probabilmente usare 'boost :: mpl :: and_' e' boost :: mpl :: or_' per combinare gli argomenti in 'disable_if'. +1 comunque :) –

+0

Puoi anche usare 'ice_and' e' ice_or' dalla libreria Boost. –

3

Rinvia a un altro modello. Ecco il modello generale per ciò che si vuole: "Cosa devo fare per accettare più di una gamma di classi di base di alcuni tratti"

template <typename T, bool HasTrait = false> 
struct scanner_impl; 

template <typename T> 
struct scanner_impl 
{ 
    // Implement as though the trait is false 
}; 

template <typename T> 
struct scanner_impl<true> 
{ 
    // Implement as though the trait is true 
}; 

// This is the one the user uses 
template <typename T> 
struct scanner : scanner_impl<T, typename has_my_trait<T>::value> 
{ 
}; 
+1

Buona idea aggiungere indirection in questo modo. Forse sarebbe ancora più flessibile se non si usasse un 'bool', ma un' enum' come trait-switch. – xtofl

+0

@xtofl: Sì. Ciò risponderebbe anche alla domanda bonus. –

Problemi correlati