Quando dovrei usare typedef in C ++?

Nei miei anni di programmazione in C ++ (MFC) non ho mai sentito il bisogno di usare typedef , quindi non so davvero a cosa serve. Dove dovrei usarlo? Ci sono situazioni reali in cui si preferisce l’uso di typedef ? O è davvero più una parola chiave C specifica?

Template Metaprogramming

typedef è necessario per molte attività di metaprogrammazione dei template – ogni volta che una class viene trattata come una “funzione di tipo a tempo di compilazione”, un typedef viene usato come un “valore di tipo in fase di compilazione” per ottenere il tipo risultante. Ad esempio, considera una metafunzione semplice per convertire un tipo di puntatore al suo tipo di base:

 template struct strip_pointer_from; template struct strip_pointer_from { // Partial specialisation for pointer types typedef T type; }; 

Esempio: l’espressione di tipo strip_pointer_from::type valutata come double . Si noti che la metaprogrammazione del modello non è comunemente utilizzata al di fuori dello sviluppo della libreria.

Semplificazione dei tipi di puntatore di funzioni

typedef è utile per dare un alias breve e preciso a tipi di puntatori a funzioni complicate:

 typedef int (*my_callback_function_type)(int, double, std::string); void RegisterCallback(my_callback_function_type fn) { ... } 

Nel libro di Bjarne afferma che è ansible utilizzare typedef per affrontare problemi di portabilità tra sistemi che hanno diverse dimensioni intere. (questa è una parafrasi)

Su una macchina dove sizeof (int) è 4 puoi

 typedef int int32; 

Quindi usa int32 ovunque nel tuo codice. Quando passi a un’implementazione di C ++ dove sizeof (int) è 2, puoi semplicemente cambiare typdef

 typedef long int32; 

e il tuo programma continuerà a funzionare sulla nuova implementazione.

utilizzare con il puntatore di funzione

Nascondi dichiarazioni del puntatore funzione Con un typedef

 void (*p[10]) (void (*)() ); 

Solo pochi programmatori possono dire che p è un “array di 10 puntatori a una funzione che restituisce vuoto e che prende un puntatore a un’altra funzione che restituisce nulla e non accetta argomenti.” La syntax ingombrante è quasi indecifrabile. Tuttavia, è ansible semplificarlo notevolmente utilizzando dichiarazioni typedef. Innanzitutto, dichiarare typedef per “puntatore a una funzione che restituisce nulla e non richiede argomenti” come segue:

  typedef void (*pfv)(); 

Quindi, dichiara un altro typedef per “puntatore a una funzione che restituisce void e che prende un pfv” in base al typedef dichiarato in precedenza:

  typedef void (*pf_taking_pfv) (pfv); 

Ora che abbiamo creato il typedef pf_taking_pfv come sinonimo per l’ingombrante “puntatore a una funzione che restituisce il vuoto e sta prendendo un pfv”, dichiarare un array di 10 puntatori del genere è un gioco da ragazzi:

  pf_taking_pfv p[10]; 

a partire dal

Solo per fornire alcuni esempi per le cose dette: contenitori STL.

  typedef std::map tFrobozMap; tFrobozMap frobozzes; ... for(tFrobozMap::iterator it=frobozzes.begin(); it!=map.end(); ++it) { ... } 

Non è inusuale nemmeno usare typedef come

 typedef tFrobozMap::iterator tFrobozMapIter; typedef tFrobozMap::const_iterator tFrobozMapCIter; 

Un altro esempio: utilizzando i puntatori condivisi:

 class Froboz; typedef boost::shared_ptr FrobozPtr; 

[aggiorna] Come per commento – dove metterli?

L’ultimo esempio – using shared_ptr – è semplice: sono veri materiali header – o almeno un header forward. In ogni caso, è necessaria la dichiarazione anticipata per shared_ptr, e uno dei suoi vantaggi dichiarati è che è sicuro da usare con un decl forward.

Detto in altro modo: se c’è un shared_ptr probabilmente dovresti usare il tipo solo attraverso un shared_ptr, quindi separare le dichiarazioni non ha molto senso.

(Sì, xyzfwd.h è un problema: li userei solo negli hotspot, sapendo che gli hotspot sono difficili da identificare. Incolpa il modello di compilazione + compilazione C ++ …)

Typedef del contenitore In genere utilizzo dove viene dichiarata la variabile del contenitore, ad esempio localmente per una var locale, come membri della class quando l’istanza del contenitore effettiva è un membro della class. Funziona bene se il tipo di contenitore reale è un dettaglio di implementazione, senza alcuna dipendenza aggiuntiva.

Se diventano parte di una particolare interfaccia, vengono dichiarati insieme all’interfaccia con cui vengono utilizzati, ad es

 // FrobozMangler.h #include "Froboz.h" typedef std::map tFrobozMap; void Mangle(tFrobozMap const & frobozzes); 

Questo diventa problematico quando il tipo è un elemento vincolante tra diverse interfacce, ovvero lo stesso tipo è necessario per più intestazioni. Alcune soluzioni:

  • dichiararlo insieme al tipo contenuto (adatto per contenitori che sono frequentemente utilizzati per questo tipo)
  • spostali in un’intestazione separata
  • passare a un’intestazione separata e trasformarla in una class di dati in cui il contenitore reale è di nuovo un dettaglio di implementazione

Sono d’accordo sul fatto che gli ultimi due non sono grandiosi, li userei solo quando mi metterò nei guai (non in modo proattivo).

typedef è utile in molte situazioni.

Fondamentalmente ti permette di creare un alias per un tipo. Quando / se devi cambiare il tipo, il resto del codice potrebbe essere immutato (dipende ovviamente dal codice, ovviamente). Ad esempio, supponiamo di voler eseguire l’iter su un vettore c ++

 vector v; ... for(vector::const_iterator i = v->begin(); i != v.end(); i++) { // Stuff here } 

In futuro potresti pensare di cambiare il vettore con un elenco, perché il tipo di operazioni che devi fare su di esso. Senza typedef devi modificare TUTTE le occorrenze del vettore all’interno del tuo codice. Ma se scrivi qualcosa del genere:

 typedef vector my_vect; my_vect v; ... for(my_vect::const_iterator i = v->begin(); i != v.end(); i++) { // Stuff here } 

Ora devi solo cambiare una riga di codice (cioè da ” typedef vector my_vect ” a ” typedef list my_vect “) e tutto funziona.

typedef ti fa risparmiare tempo anche quando hai strutture dati complesse che sono molto lunghe da scrivere (e difficili da leggere)

Un buon motivo per usare typedef è se il tipo di qualcosa può cambiare. Ad esempio, diciamo che per ora, gli inte 16 bit vanno bene per l’indicizzazione di alcuni dataset perché nel prossimo futuro avrai meno di 65535 elementi e che i vincoli di spazio sono significativi o hai bisogno di buone prestazioni della cache. Tuttavia, se hai la necessità di utilizzare il tuo programma su un set di dati con più di 65535 elementi, desideri essere in grado di passare facilmente a un numero intero più ampio. Usa un typedef e devi solo cambiarlo in un posto.

typedef consente non solo di avere un alias per tipi complessi, ma ti dà un posto naturale per documentare un tipo. A volte lo uso a scopo di documentazione.

Ci sono anche momentjs in cui uso una matrice di byte. Ora, una serie di byte potrebbe significare un sacco di cose. typedef rende pratico definire il mio array di byte come “hash32” o “fileContent” per rendere il mio codice più leggibile.

Usi del mondo reale di typedef:

  • fornire alias amichevoli per i tipi di modelli prolisso
  • fornire alias amichevoli per i tipi di puntatore a funzione
  • fornire etichette locali per tipi, ad esempio:

     template class A { typedef _T T; }; template class B { void doStuff( _T::T _value ); }; 

C’è un altro caso d’uso per usare typedef quando vogliamo abilitare un tipo di codice Container Independent (ma non esattamente!)

Diciamo che hai lezione:

 Class CustomerList{ public: //some function private: typedef list CustomerContainer; typedef CustomerContainer::iterator Cciterator; }; 

Il codice sopra incapsula l’implementazione del contenitore interno utilizzando typedef e anche se in futuro il contenitore dell’elenco deve essere modificato in vector o deque, l’utente della class CustomerList non deve preoccuparsi dell’implementazione esatta del contenitore.

Quindi, il typedef incapsula e in qualche modo ci aiuta a scrivere codice Container Independent

Ogni volta che rende la fonte più chiara o migliore da leggere.

Io uso il tipo di typedef in C # per generici / modelli. Un “NodeMapping” è solo migliore per leggere / utilizzare e capire quindi un sacco di “Dictionary “. A PARER MIO. Quindi lo consiglierei per i modelli.

Typedef consente flessibilità nella tua class. Quando si desidera modificare il tipo di dati nel programma, non è necessario modificare più posizioni, ma è sufficiente modificare un’occorrenza.

 typedef  value_type 

puoi dare il nome di nay invece di value_type , ma value_type è normalmente il nome standard.

Quindi puoi usare typedef come

 value_type i=0; //same as a int or double i=0; 

… e non hai bisogno di un typedef per un enum o una struttura.

O tu?

 typedef enum { c1, c2 } tMyEnum; typedef struct { int i; double d; } tMyStruct; 

può essere scritto meglio come

 enum tMyEnum { c1, c2 } struct tMyStruct { int i; double d; }; 

È corretto? Che mi dici di C?