2011-07-01 4 views
10

Mentre leggevo this question, mi sono imbattuto nella risposta di @Johannes.Sintassi di template diversa per trovare se l'argomento è una classe oppure no

template<typename> struct void_ { typedef void type; }; 

template<typename T, typename = void> // Line 1 
struct is_class { static bool const value = false; }; 

template<typename T> 
struct is_class<T, typename void_<int T::*>::type> { // Line 2 
    static bool const value = true; 
}; 

Questo costrutto rileva se il given type is a class or not. Quello che mi imbarazza è il nuovo tipo di sintassi per scrivere questo piccolo meta-programma. Qualcuno può spiegare in dettaglio:

  1. Perché abbiamo bisogno della linea 1?
  2. Qual è il significato della sintassi <int T::*> come parametro template nella riga 2?

risposta

10

Riga 1: Scegliere la specializzazione parziale di seguito se il test ha esito positivo.

La riga 2: int T::* è valida solo se T è un tipo di classe, poiché indica un puntatore membro.

Come tale, se è valida, void_<T>::type cede void, avendo questo parziale specializzazione scelto per l'istanziazione con un value di true. Se T non è di tipo classe, questa specializzazione parziale è fuori dall'immagine grazie a SFINAE e torna al modello generale con uno value di false.

Ogni volta che vedi un T::SOMETHING, se SOMETHING non è presente, che si tratti di un tipo, di un membro dati o di una semplice definizione di puntatore, hai ottenuto SFINAE.

+2

Perché passando per 'struct is_class :: type>' (invece del più semplice 'struct is_class ') necessario? (La seconda compilazione ma non fornisce il risultato desiderato) – AProgrammer

+0

@AProgrammer: Come si compila la seconda se non si conosce il tipo di classe?Come sceglieresti il ​​default, che in questo caso è 'typename = void'? – Xeo

+2

Perché 'void _ :: type' e il parametro predefinito nella prima versione di' is_class' (ad esempio 'typename = void') deve essere uguale? – iammilind

1

1. linea 1 viene usato per qualcosa che non è una classe, come int, long e così via ...

ad esempio:

class foo {}; 

if (is_class<foo>::value) // is a class 
    line_2 called 
else // if not 
    line 1 called 

causa di c'è una specializzazione parziale così la riga 1 è ciò che devi avere, altrimenti otterrai un errore se passi un tipo che non è una classe (come char *, long, int ...)

2: la chiave di int T: : * è ":: *", è un operatore standard in C++

significa che un puntatore punta a un membro di una classe, può essere sia una funzione che un campo dati, e in questo caso indica chiunque abbia un membro o possa lavorare con un puntatore membro, questo è funziona solo per le classi , structs o unions in C++, quindi il risultato di ciò, saprete che il parametro è o meno una classe.

btw, google alcune parole chiave come: C++ template, parziale specializzazione, e tipo tratti, o tipo di spinta tratti

speranza che questo sia utile per voi :)