Dove e perché devo mettere il “modello” e “typename” parole chiave?

In modelli, dove e perché devo mettere typename e template dipendente nomi? Che cosa sono esattamente dipendente nomi comunque? Ho il seguente codice:

template <typename T, typename Tail> //Tail will be a UnionNode too.
struct UnionNode : public Tail {
    //...
    template<typename U> struct inUnion {
        //Q: where to add typename/template here?
        typedef Tail::inUnion<U> dummy; 
    };
    template< > struct inUnion<T> {
    };
};
template <typename T> //For the last node Tn.
struct UnionNode<T, void> {
    //...
    template<typename U> struct inUnion {
        char fail[ -2 + (sizeof(U)%2) ]; //Cannot be instantiated for any U
    };
    template< > struct inUnion<T> {
    };
};

Il problema che ho è in typedef Tail::inUnion<U> dummy linea. Sono abbastanza certo che inUnion è un dipendente nome e VC++ è abbastanza di destra soffocamento su di esso. So anche che dovrei essere in grado di aggiungere template da qualche parte per indicare al compilatore che inUnion è un modello-id. Ma dove esattamente? E dovrebbe quindi assumere che inUnion è un modello di classe, cioè inUnion<U> nomi di un tipo e non una funzione?

Fastidiosa domanda: perché non boost::Variante?
Sensibilità politiche, è la portabilità.
Ho fatto la tua domanda (“Dove mettere la modello/typename?”) risaltano meglio mettendo la domanda finale e il codice all’inizio e accorciato il codice orizzontalmente per adattarsi a 1024x schermo.
Rimosso il “dipendente nomi” dal titolo perché sembra che la maggior parte delle persone che si interrogano su “typename” e “modello” non so cosa “dipendente nomi”. Dovrebbe essere meno confusione, in questo modo.
boost è abbastanza portatile. Direi che solo la politica generale è il motivo per cui spinta è spesso unembraced. L’unica buona ragione per cui so che è la maggiore i tempi di costruzione. Altrimenti questo è tutto a perdere migliaia di dollari di reinventare la ruota.

OriginaleL’autore MSalters | 2009-03-04

6 Replies
  1. 962

    Per analizzare un programma in C++, il compilatore deve conoscere se alcuni nomi sono tipi o non. Il seguente esempio dimostra che:

    t * f;

    Come dovrebbe essere analizzato? Per molte lingue, un compilatore non ha bisogno di sapere il significato di un nome per analizzare e fondamentalmente di sapere quale azione di una riga di codice. In C++, di cui sopra, tuttavia, possono produrre notevolmente differenti interpretazioni a seconda di che cosa t significa. Se si tratta di un tipo, quindi sarà una dichiarazione di un puntatore f. Tuttavia, se non è un tipo, sarà una moltiplicazione. In modo che il C++ Standard dice al paragrafo (3/7):

    Alcuni nomi denotano tipi o modelli. In generale, ogni volta che un nome viene rilevato, è necessario determinare se il nome che denota una di queste entità, prima di continuare ad analizzare il programma che lo contiene. Il processo che determina questo è chiamato il nome di ricerca.

    Come il compilatore scoprire ciò che un nome t::x si riferisce, se t si riferisce a un modello tipo di parametro? x potrebbe essere una static int membro di dati che potrebbero essere moltiplicati o potrebbe anche essere una classe nidificata o typedef che potrebbe produrre una dichiarazione. Se un nome ha questa proprietà, che non può essere guardato fino all’attuale modello di argomenti conosciuti – allora si chiama nome dipendente (“dipende” da i parametri del modello).

    Potrebbe consigliare solo aspettare fino a che l’utente crea il template:

    Aspettiamo fino a quando l’utente crea il modello, e poi scoprire il vero significato di t::x * f;.

    Questo lavoro e in realtà è consentito dalla Norma come un possibile approccio di implementazione. Questi compilatori, fondamentalmente, copiare il modello di testo in un buffer interno, e solo quando un’istanza è necessario, si analizza il modello ed eventualmente rilevare errori nella definizione. Ma invece di preoccuparsi del modello di utenti (poveri colleghi!) con gli errori fatti da un template autore, altre implementazioni di scegliere di effettuare il check modelli più presto e dare errori nella definizione il più presto possibile, prima che un’istanza prende anche il posto.

    Quindi ci deve essere un modo per dire al compilatore che alcuni nomi sono tipi e che alcuni nomi non sono.

    “Typename” parola chiave

    La risposta è: Abbiamo decidere come il compilatore dovrebbe analizzare questo. Se t::x è un nome dipendente, quindi abbiamo bisogno di anteporre da typename per indicare al compilatore di analizzare in un certo modo. Lo Standard dice a (14.6/2):

    Un nome utilizzato in un modello di dichiarazione o definizione e che dipende da un modello-parametro
    ritiene che non il nome di un tipo a meno che le condizioni di ricerca del nome trova un tipo di nome o il nome è qualificato
    la parola chiave typename.

    Ci sono molti nomi per i quali typename non è necessario, perché il compilatore è in grado, con la normativa di ricerca per nome nella definizione del modello, capire come analizzare un costrutto stesso – per esempio con T *f;, quando T è un modello del tipo di parametro. Ma per t::x * f; da una dichiarazione, deve essere scritto come typename t::x *f;. Se si omette la parola chiave e il nome è preso da un non-type, ma quando la creazione di istanze di reperti, denota un tipo, i soliti messaggi di errore vengono emessi dal compilatore. A volte, l’errore, di conseguenza, è dato al momento della definizione:

    //t::x is taken as non-type, but as an expression the following misses an
    //operator between the two names or a semicolon separating them.
    t::x f;

    La sintassi permette typename solo prima di nomi qualificati – è perciò preso per scontato che i nomi non qualificati sono sempre noti per fare riferimento a tipi di se.

    Un simile gotcha esiste per i nomi che denotano modelli, come suggerito dal testo introduttivo.

    Il “modello” parola chiave

    Ricordare l’iniziale citazione di cui sopra e come lo Standard richiede un trattamento speciale per i modelli? Prendiamo il seguente innocente esempio:

    boost::function< int() > f;

    Potrebbe sembrare ovvio per un lettore umano. Non così per il compilatore. Immaginate la seguente definizione arbitraria di boost::function e f:

    namespace boost { int function = 0; }
    int main() { 
      int f = 0;
      boost::function< int() > f; 
    }

    Che in realtà è un valido espressione! Utilizza l’operatore minore di confrontare boost::function contro zero (int()), e quindi utilizza l’operatore maggiore di confrontare il conseguente bool contro f. Tuttavia, come si può ben sapete, boost::function nella vita reale è un modello, in modo che il compilatore sa (14.2/3):

    Dopo la ricerca del nome (3.4) rileva che un nome è un modello-nome, se questo nome è seguito da un < < è
    sempre preso come l’inizio di un modello-argomento-lista e non come un nome seguito da un meno-che
    operatore.

    Ora siamo di nuovo per lo stesso problema con typename. Che cosa se non possiamo sapere con sicurezza se il nome è un modello durante l’analisi del codice? Ci sarà bisogno di inserire template immediatamente prima il nome del modello, come specificato dal 14.2/4. Questo appare come:

    t::template f<int>(); //call a function template

    I nomi dei Template non può avvenire solo dopo un :: ma anche dopo un -> o . a un membro della classe di accesso. È necessario inserire la parola chiave anche lì:

    this->template f<int>(); //call a function template

    Dipendenze

    Per le persone che sono di spessore Standardese libri sul ripiano, e che vogliono sapere esattamente che cosa stavo parlando, parlerò un po ‘ su come questo viene specificato nella Norma.

    Nel modello dichiarazioni di alcuni costrutti hanno significati diversi a seconda di quale modello di argomenti che si utilizza per creare un’istanza del modello: le Espressioni possono avere diversi tipi o i valori che le variabili possono avere diversi tipi o le chiamate di funzione potrebbe finire per chiamare funzioni diverse. Tali costruzioni sono generalmente, si dice che dipendono sui parametri del modello.

    Lo Standard definisce con precisione le regole da se un costrutto è dipendente o non. Che li separa in logicamente diversi gruppi: Uno prende i tipi, un altro prende espressioni. Le espressioni possono dipendere dal loro valore e/o il loro tipo. Così abbiamo, con esempi tipici aggiunto:

    • Tipi dipendenti (e.g: un modello del tipo di parametro T)
    • Dipendente dal valore di espressioni (e.g: un non-modello del tipo di parametro N)
    • A seconda del tipo di espressioni (e.g: un cast per un modello del tipo di parametro (T)0)

    La maggior parte delle regole sono intuitivi e sono costruiti in modo ricorsivo: Per esempio, un tipo di costruzione come T[N] è un tipo dipendente se N è un valore di espressione dipendente o T è un tipo dipendente. I dettagli di questo si può leggere nella sezione (14.6.2/1) per dipendente tipi, (14.6.2.2) per a seconda del tipo di espressioni e (14.6.2.3) per dipendente dal valore di espressioni.

    Dipendente nomi

    Standard è un po ‘ poco chiaro su ciò che esattamente è un nome dipendente. Su una lettura semplice (si sa, il principio di minimo sorpresa), il tutto si definisce come un nome dipendente è il caso speciale per i nomi delle funzioni di seguito. Ma dato che chiaramente T::x, inoltre, deve essere ricercato nella creazione di istanze di contesto, ha anche bisogno di essere un dipendente di nome (per fortuna, e la metà del C++14 il comitato ha iniziato a considerare come risolvere questa confusione di definizione).

    Per evitare questo problema, mi hanno fatto ricorso a una semplice interpretazione del testo della Norma. Di tutti i costrutti che denotano un dipendente o di tipi di espressioni, un sottoinsieme di essi rappresentano nomi. Quei nomi sono, quindi, dipendente nomi”. Un nome può assumere forme diverse – lo Standard dice:

    Un nome è un uso di un identificatore (2.11), operatore-funzione-id (13.5), di conversione-funzione-id (12.3.2), o di un modello-id (14.2) che indica un’entità o l’etichetta (6.6.4, 6.1)

    Un identificatore è una semplice sequenza di caratteri /cifre, mentre gli altri due sono il operator + e operator type forma. L’ultimo modulo è template-name <argument list>. Tutti questi sono i nomi, e dall’uso convenzionale nella Norma, un nome può includere anche qualificatori di dire quello spazio dei nomi o di classe il nome dovrebbe essere cercato.

    Un valore dipendente dalla espressione 1 + N non è un nome, ma N. Il sottoinsieme di tutti i dipendenti costrutti che sono i nomi che si chiama nome dipendente. I nomi di funzione, tuttavia, può avere significato diverso in diverse istanze di un modello, ma purtroppo non sono catturati da questa regola generale.

    Dipendente nomi di funzione

    Non è principalmente un problema di questo articolo, ma vale ancora la pena di menzionare: i nomi di Funzione sono un’eccezione che vengono gestite separatamente. Un identificatore nome della funzione non dipende da sé, ma per il tipo di argomento dipendente espressioni utilizzati in una chiamata. Nell’esempio f((T)0), f è un nome dipendente. Nella Norma, questo è specificato a (14.6.2/1).

    Ulteriori note ed esempi

    In un numero di casi sufficiente, abbiamo bisogno di typename e template. Il codice dovrebbe essere simile al seguente

    template <typename T, typename Tail>
    struct UnionNode : public Tail {
        //...
        template<typename U> struct inUnion {
            typedef typename Tail::template inUnion<U> dummy;
        };
        //...
    };

    La parola chiave template non sempre appaiono nell’ultima parte del nome. Può comparire in mezzo prima di un nome di classe che viene utilizzato come un ambito, come nel seguente esempio

    typename t::template iterator<int>::value_type v;

    In alcuni casi, le parole chiave sono vietati, come di seguito dettagliato

    • Sul nome di un dipendente della classe base non è consentito scrivere typename. Si ipotizza che il nome dato a un tipo di classe di nome. Questo è vero per entrambi i nomi alla base di un elenco di classe e la lista di inizializzazione del costruttore:

       template <typename T>
       struct derive_from_Has_type : /* typename */ SomeBase<T>::type 
       { };
    • In uso-le dichiarazioni non è possibile utilizzare template dopo l’ultima ::, e il C++ comitato detto di non lavorare su una soluzione.

       template <typename T>
       struct derive_from_Has_type : SomeBase<T> {
          using SomeBase<T>::template type; //error
          using typename SomeBase<T>::type; //typename *is* allowed
       };
    Questa risposta è stata copiata dal mio precedente FAQ che ho rimosso, perché ho scoperto che mi dovrebbe utilizzare meglio esistenti domande simili, invece di fare nuove “pseudo domande” solo per lo scopo di dare la risposta. Grazie a @Prasoon, che ha modificato le idee dell’ultima parte (i casi in cui typename/modello è vietato) nella risposta.
    Mi potete aiutare quando si usa questa sintassi? questo->modello f<int>(); ottengo questo errore “modello” (come disambiguator) è consentito solo all’interno di modelli, ma senza il modello di parola chiave, funziona benissimo.
    La presentazione di questa risposta, è semplicemente meraviglioso. Si è ben pensato e porta pausa per qualcuno che legge per mettere in dubbio quello che davvero pensano di sapere su come la lingua, le “opere”. Vorrei poter votare più di una volta.
    non manca nulla. Ma è ancora necessario per scrivere la disambigua, anche se la linea non è più ambigua.
    lo scopo è quello di mantenere la lingua e compilatori più semplice. Ci sono proposte per permettere a più situazioni automaticamente capire le cose, in modo che avete bisogno di la parola chiave meno spesso. Nota che nel tuo esempio, il token che è ambiguo e solo dopo aver visto “>” dopo il doppio, è possibile disambiguare come modello staffa angolare. Per ulteriori dettagli, io sono la persona sbagliata per chiedere, perché non ho alcuna esperienza nell’implementazione di un compilatori C++’ parser.

    OriginaleL’autore Johannes Schaub – litb

  2. 125

    C++11

    Problema

    Mentre le norme in C++03 su quando hai bisogno di typename e template sono in gran parte ragionevole, c’è un fastidioso svantaggio della sua formulazione

    template<typename T>
    struct A {
      typedef int result_type;
    
      void f() {
        //error, "this" is dependent, "template" keyword needed
        this->g<float>();
    
        //OK
        g<float>();
    
        //error, "A<T>" is dependent, "typename" keyword needed
        A<T>::result_type n1;
    
        //OK
        result_type n2; 
      }
    
      template<typename U>
      void g();
    };

    Come si può vedere, abbiamo bisogno di disambigua parola chiave, anche se il compilatore può perfettamente capire se stesso che A::result_type può essere solo int (e, quindi, è un tipo), e this->g può solo essere il modello g dichiarato più tardi (anche se A è esplicitamente specializzata da qualche parte, che non influenzano il codice all’interno del modello, in modo che il suo significato non può essere influenzata da una successiva specializzazione di A!).

    Corrente di creazione di un’istanza

    Per migliorare la situazione, in C++11 la lingua tracce quando un tipo si riferisce al allegando modello. Sapere che, il tipo deve essere stata formata una certa forma di nome, che è il suo nome (sopra, A, A<T>, ::A<T>). Un tipo a cui fa riferimento ad un nome è noto per essere il corrente di creazione di un’istanza. Ci può essere di più tipi di tutte le più attuali istanza se il tipo da cui il nome è formato è un membro/classe nidificata (quindi, A::NestedClass e A sono sia le attuali istanze).

    Basato su questo concetto, la lingua dice che CurrentInstantiation::Foo, Foo e CurrentInstantiationTyped->Foo (come A *a = this; a->Foo) sono tutti membro dell’attuale istanza se che sono membri di una classe, che è l’attuale creazione di un’istanza o di un suo non-dipendente classi base (da solo facendo il nome di ricerca immediatamente).

    Le parole chiave typename e template sono ora non è più richiesto se la qualificazione è un membro dell’attuale istanza. Un punto chiave da ricordare è che A<T> è ancora a seconda del tipo nome (dopo tutto T è anche dipendente dal tipo). Ma A<T>::result_type è noto per essere un tipo di compilatore “magicamente” guardare in questo tipo di tipi dipendenti per capire questo.

    struct B {
      typedef int result_type;
    };
    
    template<typename T>
    struct C { }; //could be specialized!
    
    template<typename T>
    struct D : B, C<T> {
      void f() {
        //OK, member of current instantiation!
        //A::result_type is not dependent: int
        D::result_type r1;
    
        //error, not a member of the current instantiation
        D::questionable_type r2;
    
        //OK for now - relying on C<T> to provide it
        //But not a member of the current instantiation
        typename D::questionable_type r3;        
      }
    };

    Che è impressionante, ma si può fare di meglio? La lingua va al di là e richiede che un’implementazione sembra di nuovo fino D::result_type quando si crea un’istanza D::f (anche se ha trovato il suo significato già al momento della definizione). Quando ormai il risultato della ricerca è diverso o risultati, nell’ambiguità, il programma è mal formato e diagnostico deve essere dato. Immaginate cosa succede se abbiamo definito C come questo

    template<>
    struct C<int> {
      typedef bool result_type;
      typedef int questionable_type;
    };

    Un compilatore è necessaria per rilevare l’errore quando si crea un’istanza D<int>::f. Così si ottiene il meglio dei due mondi: “in Ritardo” di ricerca che protegge se si potrebbe ottenere nei guai con la dipendente classi di base, e anche “Immediato” ricerca libera da typename e template.

    Unknown specializzazioni

    Nel codice di D, il nome typename D::questionable_type non è un membro dell’attuale istanza. Invece la lingua contrassegna come un membro di uno sconosciuto specializzazione. In particolare, questo è sempre il caso quando si sta facendo DependentTypeName::Foo o DependentTypedName->Foo e il tipo dipendente è non corrente di creazione di un’istanza (in questo caso il compilatore è in grado di dare il massimo e a dire “vedremo in seguito cosa Foo è) o è l’attuale creazione di istanze e il nome non è stato trovato in esso o la sua non-dipendente di classi di base e ci sono anche dipendente classi di base.

    Immaginare che cosa succede se abbiamo avuto una funzione membro h entro il sopra definiti A modello di classe

    void h() {
      typename A<T>::questionable_type x;
    }

    In C++03, la lingua è consentito catturare questo errore, perché non potrebbe mai essere un valido modo per creare un’istanza A<T>::h (di qualunque argomento si dà per T). In C++11, la lingua, ora, ha un controllo ulteriore per dare un motivo in più per i compilatori per attuare questa regola. Dal A non ha alcun dipendente classi di base, e A dichiara che nessun membro questionable_type, il nome A<T>::questionable_type è un membro dell’attuale istanza un membro di uno sconosciuto specializzazione. In questo caso, ci dovrebbe essere alcun modo che il codice potrebbe validamente compilare la creazione di istanze di tempo, in modo che la lingua vieta un nome in cui la qualifica è la corrente di creazione di un’istanza di non essere né un membro di uno sconosciuto di specializzazione, né un membro dell’attuale istanza (tuttavia, questa violazione non è ancora necessario per essere diagnosticato).

    Esempi e curiosità

    Si può provare a conoscenza questa risposta e vedere se le definizioni di cui sopra ha senso per voi su un esempio reale (sono ripetuti leggermente meno dettagliato nella risposta).

    C++11 regole di apportare le seguenti valido C++03 codice malformati (che non era previsto dal C++ comitato, ma probabilmente non sarà fisso)

    struct B { void f(); };
    struct A : virtual B { void f(); };
    
    template<typename T>
    struct C : virtual B, T {
      void g() { this->f(); }
    };
    
    int main() { 
      C<A> c; c.g(); 
    }

    Questo valido C++03 codice associare this->f per A::f alla creazione di istanze di tempo e tutto è andato bene. C++11 tuttavia immediatamente associa a B::f e richiede una doppia verifica quando si crea un’istanza, verifica se la ricerca è ancora partite. Tuttavia, quando si crea un’istanza C<A>::g, il Regola Della Dominanza si applica e di ricerca troverà A::f invece.

    OriginaleL’autore Johannes Schaub – litb

  3. 78

    PREFAZIONE

    Questo post è pensato per essere un di facile lettura alternativa a litb post.

    Lo scopo di base è la stessa; una spiegazione “Quando?” e “Perché?” typename e template deve essere applicato.


    Qual è lo scopo di typename e template?

    typename e template sono utilizzabili in condizioni diverse rispetto a quando la dichiarazione di un modello.

    Ci sono alcuni contesti in C++ dove il compilatore deve esplicitamente essere raccontata come trattare un nome, e tutti questi contesti hanno una cosa in comune; essi dipendono, almeno in una modello-parametro.

    Facciamo riferimento a tali nomi, dove non ci può essere ambiguità nell’interpretazione, come; “dipendente nomi“.

    Questo post sarà offerta una spiegazione del rapporto tra dipendente-nomi, e le due parole chiave.


    UN FRAMMENTO DICE PIÙ DI 1000 PAROLE

    Cercare di spiegare che cosa sta succedendo nei seguenti funzione di modello di, per se stessi, un amico, o forse il vostro gatto, quello che accade nel prospetto contrassegnato (Un)?

    template<class T> void f_tmpl () { T::foo * x; /* <-- (A) */ }



    Potrebbe non essere così facile come si pensa, più specificamente, il risultato della valutazione (Un) pesantemente dipende sulla definizione del tipo passato come modello-parametro T.

    Diversi Ts può cambiare drasticamente la semantica coinvolti.

    struct X { typedef int       foo;       }; /* (C) --> */ f_tmpl<X> ();
    struct Y { static  int const foo = 123; }; /* (D) --> */ f_tmpl<Y> ();


    I due diversi scenari:

    • Se creiamo un’istanza della funzione-modello con tipo X, come in (C), ci sarà una dichiarazione di un puntatore a int denominato x, ma;

    • se vogliamo creare un’istanza del modello con tipo Y, come in (D), (Un), invece, consistono in un’espressione che calcola il prodotto di 123 moltiplicato per alcuni già dichiarato la variabile x.


    LA LOGICA

    Standard C++ si preoccupa per la nostra sicurezza e il benessere, almeno in questo caso.

    Per evitare un’implementazione potenzialmente affetti da brutte sorprese, la Norma impone che dobbiamo risolvere l’ambiguità di una dipendente-nome da esplicitamente dichiarando l’intento ovunque ci piacerebbe trattare il nome come un tipo-nome, o un modello-id.

    Se nulla è specificato, il dipendente-nome sarà considerato una variabile o una funzione.


    COME GESTIRE DIPENDENTE NOMI?

    Se questo fosse un film di Hollywood, dipendente-nomi sarebbe la malattia che si diffonde attraverso il contatto con il corpo, immediatamente colpisce il suo ospite, per rendere confuso. La confusione che può portare a un mal formato perso-, erhm.. programma.

    Un dipendente-nome è qualsiasi nome che direttamente, o indirettamente, dipendono da una modello-parametro.

    template<class T> void g_tmpl () {
       SomeTrait<T>::type                   foo; //(E), ill-formed
       SomeTrait<T>::NestedTrait<int>::type bar; //(F), ill-formed
       foo.data<int> ();                         //(G), ill-formed    
    }

    Abbiamo quattro dipendente nomi nel frammento di codice:

    • E)
      • “tipo” dipende la creazione di istanze di SomeTrait<T> sono T, e;
    • F)
      • “NestedTrait”, che è un modello-id, dipende SomeTrait<T>, e;
      • “tipo” alla fine del (F) dipende NestedTrait, che dipende dal SomeTrait<T>, e;
    • G)
      • “dati”, che si presenta come un socio-modello di funzione, è indirettamente un dipendente-nome dal tipo di pippo dipende la creazione di istanze di SomeTrait<T>.

    Né di dichiarazione (E), (F) o (G) è valido se il compilatore interpreta la dipendente-nomi come variabili/funzioni (che come detto in precedenza, è quello che succede se non esplicitamente dire il contrario).

    LA SOLUZIONE

    Per rendere g_tmpl valida la definizione si deve esplicitamente indicare al compilatore che ci aspettiamo un tipo in (E), un modello-id e un tipo (F), e un modello-id (G).

    template<class T> void g_tmpl () {
       typename SomeTrait<T>::type foo;                            //(G), legal
       typename SomeTrait<T>::template NestedTrait<int>::type bar; //(H), legal
       foo.template data<int> ();                                  //(I), legal
    }

    Ogni volta che una nome indica un tipo di tutti nomi coinvolti devono essere tipo di nomi- o spazi dei nomi, con questo in mente, è abbastanza facile vedere che si applica typename all’inizio della nostra cucina completamente nome completo.

    template , tuttavia, è diverso in questo senso, perché non c’è modo di arrivare ad una conclusione come; “oh, questo è un modello di quest’altra cosa deve essere anche un modello di”. Questo significa che dobbiamo applicare template direttamente di fronte a qualsiasi nome che vorremmo trattare come tali.


    POSSO ATTACCARE SOLO IL PAROLE CHIAVE DAVANTI A QUALSIASI NOME?

    Posso solo bastone typename e template davanti a qualsiasi nome? Non voglio preoccupare il contesto in cui appaiono…” – Some C++ Developer

    Le regole, la Norma stabilisce che si possono applicare le parole chiave come lungo come avete a che fare con un qualificato-nome (K), ma se il nome non è qualificato la domanda è mal formato (L).

    namespace N {
      template<class T>
      struct X { };
    }

             N::         X<int> a; //...  legal
    typename N::template X<int> b; //(K), legal
    typename template    X<int> c; //(L), ill-formed

    Nota: Applicazione typename o template in un contesto dove non è richiesto, non è considerato una buona pratica; solo perché si può fare qualcosa, non significa che si dovrebbe.

    Inoltre ci sono dei contesti in cui typename e template sono esplicitamente consentita:

    • Quando si specificano le basi di cui una classe eredita

      Ogni nome scritto in una classe derivata identificatore di base-elenco è già trattato come un tipo-nome, specificando esplicitamente typename sia mal formato, e ridondante.

                         //.------- the base-specifier-list
       template<class T> //v
       struct Derived      : typename SomeTrait<T>::type /* <- ill-formed */ {
         ...
       };

    • Quando il modello-id è quello a cui si fa riferimento a una classe derivata utilizzando la direttiva

       struct Base {
         template<class T>
         struct type { };
       };
      
       struct Derived : Base {
         using Base::template type; //ill-formed
         using Base::type;          //legal
       };

    OriginaleL’autore Filip Roséen – refp

  4. 19
    typedef typename Tail::inUnion<U> dummy;

    Tuttavia, io non sono sicuro che tu sei l’attuazione di inUnion è corretto. Se ho capito bene, questa classe non è supposto per essere istanziato, quindi il “fail” scheda non sarà mai su seamphony non riesce. Forse sarebbe meglio che indica se il tipo è in unione o non con un semplice valore booleano.

    template <typename T, typename TypeList> struct Contains;
    
    template <typename T, typename Head, typename Tail>
    struct Contains<T, UnionNode<Head, Tail> >
    {
        enum { result = Contains<T, Tail>::result };
    };
    
    template <typename T, typename Tail>
    struct Contains<T, UnionNode<T, Tail> >
    {
        enum { result = true };
    };
    
    template <typename T>
    struct Contains<T, void>
    {
        enum { result = false };
    };

    PS: guardate Boost::Variante

    PS2: guarda typelists, in particolare nel Andrei Alexandrescu libro: C++ Moderno Design

    inUnion<U> sarebbe creata, se per caso provato a chiamare Unione<float,bool>::operator=(U) con U==int. Chiama un privato set(U, inUnion<U>* = 0).
    E il lavoro con risultato=true/false è che avevo bisogno di boost::enable_if< >, che è incompatibile con la nostra attuale OSX toolchain. Il template separato è ancora una buona idea, anche se.
    Luc significa che il typedef Coda: inUnion<U> dummy; la linea. che verrà creata un’istanza di Coda. ma non inUnion<U>. essa viene creata quando si deve la definizione completa. che succede per esempio se si prende il sizeof, o accedere a un membro (utilizzando ::foo). @MSalters comunque, hai un altro problema:
    -sizeof(U) non è mai negativa 🙂 perché size_t è un tipo unsigned integer. ti verrà fornito un numero molto elevato. probabilmente si vuole fare sizeof(U) >= 1 ? -1 : 1 o simili 🙂
    vorrei solo lasciare indefinito e solo dichiarare: modello<typename U> struct inUnion; in modo che certamente non può essere istanziata. penso di avere con il sizeof, il compilatore è consentito anche a dare un errore, anche se è non istanziare, perché se sa sizeof(U) è sempre >=1 e …

    OriginaleL’autore Luc Touraille

  5. 16


    Questa risposta è pensato per essere un piuttosto breve e dolce, per rispondere (in parte) dal titolo questione. Se vuoi una risposta più dettagliata che spiega perché è necessario li ha messi lì, si prega di andare qui.


    La regola generale per mettere il typename parola chiave è soprattutto quando si utilizza un parametro di modello e si desidera accedere a un nidificati typedef o utilizzando-alias, ad esempio:

    template<typename T>
    struct test {
        using type = T; //no typename required
        using underlying_type = typename T::type //typename required
    };

    Di notare che questo vale anche per meta funzioni o cose che prendere il modello generico parametri. Tuttavia, se il parametro di modello fornito è un tipo esplicito, quindi non è necessario specificare typename, per esempio:

    template<typename T>
    struct test {
        //typename required
        using type = typename std::conditional<true, const T&, T&&>::type;
        //no typename required
        using integer = std::conditional<true, int, float>::type;
    };

    Le regole generali per aggiungere il template qualifier sono per lo più simili, tranne che comportano in genere basati su modelli, le funzioni membro (statico o altro) di una struttura/classe che di per sé è basato su modelli, per esempio:

    Dato questa struttura e funzione:

    template<typename T>
    struct test {
        template<typename U>
        void get() const {
            std::cout << "get\n";
        }
    };
    
    template<typename T>
    void func(const test<T>& t) {
        t.get<int>(); //error
    }

    Tentando di accedere t.get<int>() dall’interno della funzione, verrà generato un errore:

    main.cpp:13:11: error: expected primary-expression before 'int'
         t.get<int>();
               ^
    main.cpp:13:11: error: expected ';' before 'int'

    Così, in questo contesto, è necessario che il template parola chiave in anticipo e chiamare in questo modo:

    t.template get<int>()

    Che modo il compilatore analizza correttamente questo piuttosto che t.get < int.

    OriginaleL’autore Rapptz

  6. 2

    Metto JLBorges eccellente risposta a una simile domanda verbatim da cplusplus.com, in quanto è la più succinta spiegazione che ho letto sull’argomento.

    In un modello che scriviamo, ci sono due tipi di nomi che potrebbero essere utilizzati dipendente di nomi e non dipendente nomi. Un dipendente di nome è un nome che dipende da un parametro di modello; un non-dipendente di nome ha lo stesso significato, a prescindere di ciò che i parametri del modello sono.

    Per esempio:

    template< typename T > void foo( T& x, std::string str, int count )
    {
        //these names are looked up during the second phase
        //when foo is instantiated and the type T is known
        x.size(); //dependant name (non-type)
        T::instance_count ; //dependant name (non-type)
        typename T::iterator i ; //dependant name (type)
    
        //during the first phase, 
        //T::instance_count is treated as a non-type (this is the default)
        //the typename keyword specifies that T::iterator is to be treated as a type.
    
        //these names are looked up during the first phase
        std::string::size_type s ; //non-dependant name (type)
        std::string::npos ; //non-dependant name (non-type)
        str.empty() ; //non-dependant name (non-type)
        count ; //non-dependant name (non-type)
    }

    Che un dipendente di nome si riferisce potrebbe essere qualcosa di diverso per ogni diversa istanza del modello. Come conseguenza, i modelli C++ sono soggetti a “due fasi di ricerca del nome”. Quando un modello è inizialmente analizzato (prima di ogni istanziazione avviene) il compilatore cerca il non-dipendente nomi. Quando una particolare istanza del modello avviene, i parametri del modello sono noti da allora, e il compilatore cerca dipendente nomi.

    Durante la prima fase, il parser deve sapere se un dipendente è il nome di un tipo o il nome di una non-tipo. Per impostazione predefinita, un dipendente di nome, assunto il nome di una non-tipo. La parola chiave typename prima di un dipendente di nome, specifica che è il nome di un tipo.


    Riepilogo

    Utilizzare la parola chiave typename solo nel modello di dichiarazioni e definizioni di cui si dispone di un qualificato nome che si riferisce ad un tipo e dipende da un parametro di modello.

    OriginaleL’autore Nik-Lz

Lascia un commento