Differenza delle parole chiave "typename" e "class" nei template?


504

Per i modelli ho visto entrambe le dichiarazioni:

template < typename T >
template < class T >

Qual è la differenza?

E cosa significano esattamente quelle parole chiave nel seguente esempio (tratto dall'articolo di Wikipedia in tedesco sui modelli)?

template < template < typename, typename > class Container, typename Type >
class Example
{
     Container< Type, std::allocator < Type > > baz;
};

Risposte:


430

typenamee classsono intercambiabili nel caso di base per specificare un modello:

template<class T>
class Foo
{
};

e

template<typename T>
class Foo
{
};

sono equivalenti.

Detto questo, ci sono casi specifici in cui esiste una differenza tra typenameeclass .

Il primo è nel caso di tipi dipendenti. typenameviene utilizzato per dichiarare quando si fa riferimento a un tipo nidificato che dipende da un altro parametro del modello, come ad esempio typedefin questo esempio:

template<typename param_t>
class Foo
{
    typedef typename param_t::baz sub_t;
};

Il secondo che mostri effettivamente nella tua domanda, anche se potresti non rendertene conto:

template < template < typename, typename > class Container, typename Type >

Quando si specifica un modello di modello , la classparola chiave DEVE essere utilizzata come sopra - in questo caso non è intercambiabile (nota: poichétypename in questo caso C ++ 17 sono consentite entrambe le parole chiave) .

È inoltre necessario utilizzare classquando si crea un'istanza esplicita di un modello:

template class Foo<int>;

Sono sicuro che ci sono altri casi che mi sono perso, ma la linea di fondo è: queste due parole chiave non sono equivalenti e questi sono alcuni casi comuni in cui è necessario utilizzare l'uno o l'altro.


45
Quest'ultimo è praticamente un caso speciale del fatto che è necessario utilizzare class o struct, non typename, per definire una classe. Ovviamente nessuno dei tuoi primi due bit di codice può essere sostituito template <typename T> typename Foo {};, perché Foo <T> è sicuramente una classe.
— Steve Jessop,

2
std::vector<int>::value_typenon è un tipo dipendente, non è necessario typenamelì - ne hai bisogno solo se un tipo dipende da un parametro modello, ad esempiotemplate<class T> struct C { typedef typename std::vector<T>::value_type type; };
— Georg Fritzsche,

2
E ancora, param_tnon è un tipo dipendente. I tipi dipendenti sono nomi che dipendono da un parametro del modello , ad esempio foo<param_t>::some_type, non dai parametri del modello stessi.
— Georg Fritzsche,

2
Una proposta C40 1z N4051 ti permetterà di usare typename, ad es template <typename> typename C.
— user4112979,

4
A partire da oraGCC 5 , G ++ consente il nome tipografico in un parametro di modello di modello .
— Chnossos,

95

Per la denominazione dei parametri del modello typenamee classsono equivalenti. §14.1.2:

Non esiste alcuna differenza semantica tra classe e typename in un parametro template.

typenametuttavia è possibile in un altro contesto quando si usano i template - per suggerire al compilatore che ci si riferisce a un tipo dipendente. §14.6.2:

Si presume che un nome utilizzato in una dichiarazione o definizione di modello che dipende da un parametro modello non assegni un tipo a meno che la ricerca del nome applicabile non trovi un nome di tipo o il nome non sia qualificato dalla parola chiave typename.

Esempio:

typename some_template<T>::some_type

Senza typenameil compilatore non si può dire in generale se ci si riferisce a un tipo o no.


2
Comprendo la regola, ma cosa impedisce esattamente al compilatore di trattare some_template <T> come un tipo internamente? Scusa se mi manca qualcosa di ovvio.
— Batbrat,

23

Sebbene non vi sia alcuna differenza tecnica, ho visto i due usati per indicare cose leggermente diverse.

Per un modello che dovrebbe accettare qualsiasi tipo come T, inclusi gli incorporati (come un array)

template<typename T>
class Foo { ... }

Per un modello che funzionerà solo dove T è una vera classe.

template<class T>
class Foo { ... }

Ma tieni presente che questa è puramente una cosa di stile che alcune persone usano. Non richiesto dalla norma o applicato dai compilatori


15
Non ti biasimo per averlo menzionato, ma penso che questa politica sia piuttosto sbagliata, dal momento che i programmatori finiscono per prendersi il tempo a pensare a qualcosa che non ha importanza ("ho usato quello giusto?") Per indicare qualcosa che non ' non importa ("esiste un tipo incorporato che implementa l'interfaccia richiesta per questo parametro template?"). Se vengono utilizzati membri del parametro template ( T t; int i = t.toInt();), è necessaria una "classe reale" e il codice non verrà compilato se si fornisce intper T...
— Steve Jessop,

1
Se vuoi limitare l'uso alle classi effettive, è meglio aggiungere una specializzazione per lanciare / causare un errore per i tipi non di classe. Se vuoi limitare l'uso a determinate classi, specializza solo per loro. In ogni caso, una tale distinzione stilistica è troppo sottile per far passare il messaggio.
— Potatoswatter

2
Dal momento che intendevano la stessa cosa, per favore usane solo una. Altrimenti, è come usare inline {, a meno che non sia un martedì, e poi usi la riga successiva {.
— Paul Draper,

+1 Lo faccio anch'io a volte ... classimplica che non ti stai solo aspettando un "valore", forse supportando alcuni operatori, copiando o spostando la costruzione e / o il compito, ma in particolare hai bisogno di un tipo che supporti alcune semantiche di accesso dei membri. La rapida occhiata alla dichiarazione fissa quindi aspettative e scoraggi, ad esempio fornendo tipi classpredefiniti per i parametri quando ciò sarebbe certamente un errore.
— Tony Delroy,

Mi piacerebbe capire quali tipi di situazioni del mondo reale esistono in cui un modello funzionerebbe per QUALSIASI classe, ma non funzionerebbe con tipi incorporati. Hai un esempio?
— lfalin,

7
  1. Nessuna differenza
  2. Il parametro del tipo di modello Containerè esso stesso un modello con due parametri di tipo.

3
c'è una differenza in generale.
— Hassan Syed,

potrebbero anche essere nominati quei due parametri con cui è stato modellato il contenitore? nell'esempio non hanno alcun nome. E anche - in questo esempio è scritto "contenitore di classe" - potrebbe invece essere scritto anche "contenitore di nomi"?
— Mat

2
@Mat: sì, il termine da cercare è parametri / argomenti del modello di modello . Ad esempio:template<template<class U> class V> struct C {};
— Georg Fritzsche,

6

Questo frammento è tratto dal libro di primer c ++. Anche se sono sicuro che sia sbagliato.

Ogni parametro di tipo deve essere preceduto dalla classe di parole chiave o dal nome di battitura:

// error: must precede U with either typename or class
template <typename T, U> T calc(const T&, const U&);

Queste parole chiave hanno lo stesso significato e possono essere utilizzate in modo intercambiabile all'interno di un elenco di parametri del modello. Un elenco di parametri del modello può utilizzare entrambe le parole chiave:

// ok: no distinction between typename and class in a template parameter list
template <typename T, class U> calc (const T&, const U&);

Può sembrare più intuitivo utilizzare la parola chiave typename piuttosto che class per designare un parametro di tipo template. Dopotutto, possiamo usare i tipi predefiniti (non di classe) come argomento del tipo di modello. Inoltre, typename indica più chiaramente che il nome che segue è un nome di tipo. Tuttavia, typename è stato aggiunto a C ++ dopo che i template erano già ampiamente utilizzati; alcuni programmatori continuano a usare esclusivamente la classe

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.