2016-05-09 12 views
13

Codice campione 1:differenza tra i parametri del template typename vs non typenames?

namespace detail { 
    enum enabler { dummy }; 
} 

class foo { 
public: 
    template <typename T, 
       typename std::enable_if<!std::is_integral<T>::value, 
             detail::enabler>::type = detail::enabler::dummy> 
    void func(T t) { 
     std::cout << "other" << std::endl; 
    } 

    template <typename T, 
       typename std::enable_if<std::is_integral<T>::value, 
             detail::enabler>::type = detail::enabler::dummy> 
    void func(T t) { 
     std::cout << "integral" << std::endl; 
    } 
}; 

Esempio di codice 2:

namespace detail { 
    enum enabler { dummy }; 
} 

class foo { 
public: 
    template <typename T, 
       typename T2 = typename std::enable_if<!std::is_integral<T>::value, detail::enabler>::type> 
    void func(T t) { 
     std::cout << "other" << std::endl; 
    } 

    template <typename T, 
       typename T2 = typename std::enable_if<std::is_integral<T>::value, detail::enabler>::type> 
    void func(T t) { 
     std::cout << "integral" << std::endl; 
    } 
}; 

capisco perché campione 2 non può essere compilato. Fondamentalmente perché entrambe le funzioni template sono uguali tra loro (hanno anche gli stessi parametri del modello T, T2).

Non capisco perché l'esempio 1 compili! Vedo che è lo stesso problema. Fondamentalmente invece di avere il secondo parametro template come typename, è una enum invece di una typename questa volta.

Qualcuno può spiegare per favore?

Inoltre, ho eseguito il seguente codice e restituito vero che è ancora più confuso! (Senso che è vero, ma confondendo quel campione 1 compilazioni)

std::cout 
    << std::boolalpha 
    << std::is_same<std::enable_if<std::is_integral<int>::value, int>::type, 
        std::enable_if<!std::is_integral<float>::value, int>::type>::value 
    << std::endl; 
+0

Questo in realtà non riguarda il requisito di 'typename' tanto quanto è una domanda su come funziona SFINAE. – Niall

+0

Cosa c'è di sorprendente nell'ultimo bit? Abbiamo 'enable_if :: type', due volte. – Barry

risposta

7

Gli argomenti predefiniti (se argomenti della funzione predefinita o argomenti del modello predefinito) non fanno parte della firma della funzione. È consentito definire una sola funzione con una determinata firma.

Nel codice di esempio 1, se togliamo i nomi degli argomenti e tutte le impostazioni predefinite, abbiamo due funzioni che sono:

template <typename T, std::enable_if_t<!std::is_integral<T>::value, detail::enabler>> 
void func(T) { ... } 

template <typename T, std::enable_if_t<std::is_integral<T>::value, detail::enabler>> 
void func(T) { ... } 

Quelli sono due firme diverse - il parametro secondo modello ha diversi tipi di entrambe le funzioni - quindi è valido da questa prospettiva.

Ora, cosa succede quando effettivamente lo chiamiamo. Se T è un tipo integrale, il secondo argomento viene sostituito in entrambi i casi:

template <typename T, ????>     void func(T) { ... } 
template <typename T, detail::enabler = dummy> void func(T) { ... } 

Nella prima funzione, l'espressione typename std::enable_if<false, detail::enabler>::type è mal formata. Non c'è type typedef su quel tipo. In genere, scrivere codice mal formato è un errore del compilatore. Tuttavia, grazie a una regola chiamata SFINAE (Errore di sostituzione non è un errore), il codice non formato che si verifica nel contesto immediato della sostituzione del modello non è un errore, ma causa solo la rimozione della specializzazione del modello di funzione/classe dall'insieme di considerazioni .Così finiamo con una sola valida:

template <typename T, detail::enabler = dummy> void func(T) { ... } 

che viene chiamato.


Tuttavia, nel codice di esempio 2, abbiamo queste due funzioni:

template <typename T, typename> 
void func(T) { ... } 

template <typename T, typename> 
void func(T) { ... } 

Quelli sono la stessa cosa! Stiamo definendo la stessa funzione due volte, il che non è consentito, quindi l'errore. Questo è un errore per lo stesso motivo:

int foo(int x = 1) { return x; } 
int foo(int x = 2) { return x; } 

è un errore.


Questo è il motivo per cui Xeo utilizzato l'enum (unconstructible) per consentire, se per cominciare - rende più facile scrivere disgiunti specializzazioni modello di funzione dal momento che si può fare con l'enum, ma non è possibile farlo con tipi.

Nota che si potrebbe allo stesso modo basta fare lo stesso con qualcosa di simile int:

template <class T, std::enable_if_t<std::is_integral<T>::value, int> = 0> 
void func(T) { ... } 

Ma questo permette ad un utente del male per fornire un valore per tale argomento (non avrebbe importanza in questo particolare contesto, ma potrebbe in altri). Con detail::enabler, non è possibile fornire questo valore.

+0

Beh, non ho alcun problema con gli ultimi due pezzi di codice. Ma il problema è con il primo. Come mai il secondo parametro in entrambi non è lo stesso? – mkmostafa

+1

Non è corretto affermare che il codice di esempio 1 funzioni perché le funzioni hanno diverse firme. – user2296177

+0

@mkmostafa Perché dovrebbero essere uguali? Mi sembrano diversi. – Barry

2

Credo che sono arrivato a una conclusione (forse nessuno ha potuto verificare o aggiornare):

In campione 2:

Quando il il compilatore incontra una chiamata di funzione e prova ad associarlo a un modello trova due modelli con i parametri typename T e typename T2 (i valori predefiniti non contano, questo è normale) e ciò crea un'ambiguità.

In campione 1:

Quando il compilatore enounters una funzione chiamano cercherà di corrispondere con uno dei due modelli, uno di loro riuscirà quindi avrà typename T e detail::enabler e l'altro non avrà valore definito per il secondo parametro del modello in modo che l'ambiguità venga rimossa in questo caso.

Problemi correlati