2015-01-27 16 views
5

Versione breve: Ho una funzione template, che è "universale", ma voglio forzare l'utente a specificare esplicitamente il tipo dell'argomento, passano come parametro a questa funzione.Esiste un modo per forzare l'utente a specificare esplicitamente il tipo di argomento del modello?

Qualche idea?


Versione lunga: suona come un disegno terribile, ma qui è il mio caso e non riesco a pensare a qualcosa di meglio in questo momento.

Sto cercando di "implementare" ::setsockopt in una piccola classe socket (e non voglio avere tonnellate di funzioni, prendere argomenti diversi e fare lo stesso). Per esempio:

template< typename OPTION_VALUE_TYPE > 
bool set_option(int level, int option_name, const OPTION_VALUE_TYPE& value) 
{ 
    return -1 != ::setsockopt(fd_, level, option_name, &value, sizeof(value)); 
} 

MA, questo potrebbe portare alla seguente situazione - invocando set_option con 1, cercando di impostare unsigned char opzione porterebbe al fallimento, come è 1int. uso corretto sarebbe:

set_option< unsigned char >(level, option, 1); 

come

set_option(level, option, 1); 

compilerà perfettamente bene, ma sarà terribilmente sbagliato.

risposta

4

Sì, basta utilizzare il parametro di modello in un modo da cui non si può dedurre dagli argomenti. Un modo comune di fare che sta usando una typedef in una classe template:

template <typename T> 
struct identity { 
    typedef T type; 
}; 

template <typename OPTION_VALUE_TYPE> 
bool set_option(int level, int option_name, 
       typename identity<const OPTION_VALUE_TYPE&>::type value); 

di utilizzare un tipo già presente nella libreria standard, è possibile utilizzare enable_if:

template <typename OPTION_VALUE_TYPE> 
bool set_option(int level, int option_name, 
       typename std::enable_if<true, const OPTION_VALUE_TYPE&>::type value); 

La ragione per questo doesn' t consentire la deduzione di tipo argomento è perché il compilatore non può escludere specializzazioni identity: il compilatore non può escludere stai facendo

template <> 
struct identity<U> { typedef V type; }; 

wh prima che identity<U>::type non fosse più U.

+2

Altre opzioni dalla libreria standard sarebbero utilizzare 'typename add_const :: type &' or 'typename add_lvalue_reference :: type', che sono probabilmente un po 'più chiari. Usare 'enable_if' potrebbe confondere le persone nel pensare che ci sia qualche SFINAE coinvolto. –

+2

@JonathanWakely Questo è un buon punto. D'altro canto, l'uso di 'add_const' o' add_lvalue_reference' non rende chiaro che sta succedendo qualcosa di speciale, e potrebbe indurre qualcuno a fare una pulizia del codice pensando che potrebbe essere semplicemente semplificata con l'ortografia di 'const OPTION_VALUE_TYPE &'. Penso che ci sia qualcosa da dire per entrambi, e penso che l'approccio più chiaro sia quello di creare la classe helper 'identity', e semplicemente dimenticare i tipi di libreria standard. – hvd

+0

Sì, buon punto. 'identity' non ha altro scopo se non quello di farlo, quindi è la soluzione più espressiva. –

4

Mettere il tipo di modello in un contesto non dedotta come nell'esempio seguente:

template <typename T> struct identity { using type = T; }; 

template <typename OPTION_VALUE_TYPE> 
bool set_option(int level, 
       int option_name, 
       const typename identity<OPTION_VALUE_TYPE>::type& value); 
Problemi correlati