2015-11-17 16 views
12

Si consideri il seguente:Template visibilità alias nella classe annidata

template<typename X> 
struct Z {}; 

struct A 
{ 
    using Z = ::Z<int>; 

    struct B : Z 
    { 
     using C = Z; 
    }; 
}; 

Questo compila bene. Bello. Ma ora aggiungere un altro parametro nel Z:

template<typename X, typename Y> 
struct Z {}; 

struct A 
{ 
    template<typename X> 
    using Z = ::Z<X, int>; 

    struct B : Z<B> 
    { 
     using C = Z<B>; // error: too few template arguments for class template 'Z' 
    }; 
}; 

Ok, forse ha senso che la definizione di template alias Z in classe A è visibile quando derivante classe annidata B, ma non all'interno del suo corpo, innescando l'errore dal momento che la la definizione globale di Z ha due parametri.

Ma perché è il diverso comportamento nel primo caso, quando Z è solo un tipo alias in A?

Infine, fanno A un modello:

template<typename X, typename Y> 
struct Z {}; 

template<typename T> 
struct A 
{ 
    template<typename X> 
    using Z = ::Z<X, int>; 

    struct B : Z<B> 
    { 
     using C = Z<B>; 
    }; 
}; 

Ora l'errore è andato. Perché?

(Testato su Clang 3.6 e GCC 4.9.2)

risposta

9

In breve: derivante da una specializzazione del Z introduce il iniettato-class-name di ::Z, che si trova prima il modello alias. Se A è un modello, tuttavia, il nome classe iniettato non viene più trovato perché la classe base di B dipende.


Considerare [temp.local]/1:

Come (non) modello normali lezioni, i modelli di classe hanno un -class-name iniettato (Clausola 9). Il iniettato-nome-classe può essere utilizzato come modello nome o un nome-tipo.

E [basic.lookup]/3:

Il -classe-nome iniettato di una classe (Clausola 9) è anche considerato un membro della classe che ai fini di nome [...] ricerca.

Z è considerato un nome non qualificato; [Basic.lookup.unqual]/7:

enter image description here

Pertanto, la classe nome iniettato Z si trova come membro della classe base Z<B, int>, e utilizzato come modello-nome, che rende malformato il tuo secondo programma.In realtà, il primo frammento utilizza il iniettato-class-name, come pure - il seguente frammento non verrà compilato:

struct A 
{ 
    using Z = ::Z<float>; 
    struct B : ::Z<int> 
    { 
     static_assert(std::is_same<Z, ::Z<float>>{}, ""); 
    }; 
}; 

Infine, se A è realizzato un modello, si noti che B è un tipo dipendente come per [temp.dep.type]/(9.3) , quindi Z<B> è un tipo dipendente come per [temp.dep.type]/(9.7), pertanto la classe di base Z<B> non viene esaminata durante la ricerca dello ID non validoZ in base a [temp.dep]/3:

Nella definizione di una classe [..], la portata di una classe di base dipendente (14.6.2.1) non viene esaminato durante qualificato ricerca di nome sia al momento della definizione del modello di classe o membro o durante un'istanza del modello di classe o membro.

qui l'iniettato classe nomenon verrà trovato.


B è una "classe annidata [..] che è un dipendente membro del l'istanza corrente " (sottolineatura mia), dal momento che

Un nome è un dipendente membro dell'istanziazione corrente se è un membro dell'istanza corrente che, quando cercato, fa riferimento a almeno un membro di una classe che rappresenta l'istanza corrente.

+0

Wow. Questo è abbastanza chiaro, grazie. L'errore è apparso quando 'A' smesso di essere un modello, che ho pensato avrebbe semplificato molto il codice. Tuttavia, ora sono costretto a usare due nomi diversi per i due 'Z', il che rende il codice più brutto. Se c'è qualche soluzione migliore, per favore fatemelo sapere. – iavr

+0

@iavr E riguardo 'usando C = Z;'? (Non funzionerà per il fatto che 'A' sia un modello) – Columbo

+0

Ora è impressionante :-) Sì, funziona su questo codice semplificato qui, ma non sul mio originale (' nome tipo sconosciuto 'Z''). Dovrò controllare dov'è la differenza. 'A' non è più un modello, e ho intenzione di mantenerlo in questo modo. – iavr