Qual è la differenza tra "typedef" e "using" in C ++ 11?


905

So che in C ++ 11 ora possiamo usare usingper scrivere alias di tipo, come typedefs:

typedef int MyInt;

Da quanto ho capito, equivale a:

using MyInt = int;

E quella nuova sintassi è emersa dallo sforzo di avere un modo di esprimere " template typedef":

template< class T > using MyType = AnotherType< T, MyAllocatorType >;

Ma, con i primi due esempi non template, ci sono altre sottili differenze nello standard? Ad esempio, typedefaliasiamo in modo "debole". Cioè non crea un nuovo tipo ma solo un nuovo nome (le conversioni sono implicite tra quei nomi).

È lo stesso con usingo genera un nuovo tipo? Ci sono differenze?


215
Personalmente preferisco la nuova sintassi perché è molto più simile alla normale assegnazione di variabili, migliorando la leggibilità. Ad esempio, preferisci typedef void (&MyFunc)(int,int);o using MyFunc = void(int,int);?
Matthieu M.,

13
Sono pienamente d'accordo, utilizzo solo la nuova sintassi ora. Ecco perché lo stavo chiedendo, per essere sicuro che non ci sia davvero alcuna differenza.
Klaim,

80
@MatthieuM. quei due sono diversi tra loro. Dovrebbe essere typedef void MyFunc(int,int);(che in realtà non sembra così male), oppureusing MyFunc = void(&)(int,int);
R. Martinho Fernandes

5
@ R.MartinhoFernandes perché hai bisogno di (&) in using MyFunc = void(&)(int,int);? significa che MyFuncè un riferimento a una funzione? cosa succede se si omette il & ?
Rich

7
Sì, è un riferimento di funzione. È equivalente a typedef void (&MyFunc)(int,int);. Se si omette &è equivalente atypedef void MyFunc(int,int);
R. Martinho Fernandes,

Risposte:


585

Sono equivalenti, dallo standard (enfasi mio) (7.1.3.2):

Un nome typedef può anche essere introdotto da una dichiarazione alias. L'identificatore che segue la parola chiave using diventa un typedef-name e l'attributo-specificifier-seq che segue l'identificatore appartiene a quel typedef-name. Ha la stessa semantica come se fosse stata introdotta dallo specificatore typedef. In particolare, non definisce un nuovo tipo e non deve apparire nell'identificativo.


24
Dalla risposta, la usingparola chiave sembra essere un superset di typedef. Quindi, sarà typdefdeprecato in futuro?
iammilind,

49
La deprecazione non indica necessariamente l' intenzione di rimuovere - si limita semplicemente a una raccomandazione molto forte di preferire un altro mezzo.
Salvatore di ferro

18
Ma poi mi chiedo perché non hanno solo permesso a typedef di essere modellato. Ricordo di aver letto da qualche parte che avevano introdotto la usingsintassi proprio perché la typedefsemantica non funzionava bene con i template. Il che in qualche modo contraddice il fatto che usingsi definisce esattamente la stessa semantica.
Celtschk,

12
@celtschk: il motivo è menzionato nella proposta n1489. Un alias modello non è un alias per un tipo, ma un alias per un gruppo di modelli. Per fare una distinzione tra typedefi feltro un bisogno di nuova sintassi. Inoltre, tenere presente che la domanda del PO riguarda la differenza tra le versioni non modello.
Jesse Good,

4
Quindi perché questa ridondanza è stata introdotta? 2 sintassi per lo stesso scopo. E non vedo typdefmai essere deprecato.
Benoît,

237

Sono in gran parte uguali, tranne che:

La dichiarazione alias è compatibile con i modelli, mentre il typedef in stile C non lo è.


29
Appassionato in particolare della semplicità della risposta e sottolineando l'origine della composizione.
g24l,

1
@ g24l intendi typedef ... probabilmente
Marc.2377

Qual è la differenza tra C e C ++ typedefse posso chiedere?
McSinyx il

196

La sintassi di utilizzo ha un vantaggio quando viene utilizzata all'interno dei modelli. Se è necessaria l'astrazione del tipo, ma è necessario mantenere i parametri del modello per poter essere specificati in futuro. Dovresti scrivere qualcosa del genere.

template <typename T> struct whatever {};

template <typename T> struct rebind
{
  typedef whatever<T> type; // to make it possible to substitue the whatever in future.
};

rebind<int>::type variable;

template <typename U> struct bar { typename rebind<U>::type _var_member; }

L' uso della sintassi semplifica questo caso d'uso.

template <typename T> using my_type = whatever<T>;

my_type<int> variable;
template <typename U> struct baz { my_type<U> _var_member; }

35
Ho già indicato questo nella domanda però. La mia domanda è se non usi template c'è qualche differenza con typedef. Come, ad esempio, quando usi "Foo foo {init_value};" invece di "Foo foo (init_value)" si suppone che entrambi facciano la stessa cosa ma non si comportino esattamente con le stesse regole. Quindi mi chiedevo se ci fosse una simile differenza nascosta nell'uso di / typedef.
Klaim,

24

Sono essenzialmente gli stessi ma usingfornisce alias templatesche è abbastanza utile. Un buon esempio che ho trovato è il seguente:

namespace std {
 template<typename T> using add_const_t = typename add_const<T>::type;
}

Quindi, possiamo usare std::add_const_t<T>invece ditypename std::add_const<T>::type


Per quanto ne so, è un comportamento indefinito aggiungere qualcosa all'interno dello spazio dei nomi
standard

5
@someonewithpc Non aggiungevo nulla, esiste già, stavo solo mostrando un esempio di utilizzo del nome. Si prega di controllare en.cppreference.com/w/cpp/types/add_cv
Validus Oculus

9

So che il poster originale ha un'ottima risposta, ma per chiunque inciampa in questo thread come me, c'è una nota importante della proposta che penso aggiunge qualcosa di valore alla discussione qui, in particolare alle preoccupazioni nei commenti se la typedefparola chiave è sarà contrassegnato come obsoleto in futuro o rimosso per essere ridondante / vecchio:

È stato suggerito di (ri) usare la parola chiave typedef ... per introdurre gli alias dei template:

template<class T>
  typedef std::vector<T, MyAllocator<T> > Vec;

Tale notazione ha il vantaggio di utilizzare una parola chiave già nota per introdurre un alias di tipo. Tuttavia, mostra anche diversi svantaggi [sic] tra cui la confusione dell'uso di una parola chiave nota per introdurre un alias per un nome-tipo in un contesto in cui l'alias non designa un tipo, ma un modello; nonVec è un alias per un tipo e non dovrebbe essere preso per un nome typedef. Il nome è un nome per la famiglia - dove il punto elenco è un segnaposto per un tipo-nome. Di conseguenza non proponiamo la sintassi "typedef". D'altra parte la fraseVecstd::vector<•, MyAllocator<•> >

template<class T>
  using Vec = std::vector<T, MyAllocator<T> >;

può essere letto / interpretato come: da ora in poi, userò Vec<T>come sinonimo distd::vector<T, MyAllocator<T> > . Con questa lettura, la nuova sintassi per l'aliasing sembra ragionevolmente logica.

Per me, questo implica un supporto continuo per la typedefparola chiave in C ++ perché può ancora rendere il codice più leggibile e comprensibile .

L'aggiornamento della usingparola chiave era specifico per i modelli e (come indicato nella risposta accettata) quando si lavora con non modelli usinge typedefsono meccanicamente identici, quindi la scelta spetta totalmente al programmatore sulla base della leggibilità e della comunicazione dell'intento .


2

Entrambe le parole chiave sono equivalenti, ma ci sono alcuni avvertimenti. Uno è che dichiarare un puntatore a funzione con using T = int (*)(int, int);è più chiaro che con typedef int (*T)(int, int);. Il secondo è che la forma alias modello non è possibile con typedef. Terzo è che esporre l'API C richiederebbe typedefnelle intestazioni pubbliche.


0

Le dichiarazioni typedef possono, mentre le dichiarazioni alias non possono essere utilizzate come dichiarazioni di inizializzazione

Ma, con i primi due esempi non modello, ci sono altre sottili differenze nello standard?

Sebbene sia un caso angolare, una dichiarazione typedef è un'istruzione init e può quindi essere utilizzata in contesti che consentono dichiarazioni di inizializzazione

// C++11 (C++03) (init. statement in for loop iteration statements).
for(typedef int Foo; Foo{} != 0;) {}

// C++17 (if and switch initialization statements).
if (typedef int Foo; true) { (void)Foo{}; }
//  ^^^^^^^^^^^^^^^ init-statement

switch(typedef int Foo; 0) { case 0: (void)Foo{}; }
//     ^^^^^^^^^^^^^^^ init-statement

// C++20 (range-based for loop initialization statements).
std::vector<int> v{1, 2, 3};
for(typedef int Foo; Foo f : v) { (void)f; }
//  ^^^^^^^^^^^^^^^ init-statement

mentre una dichiarazione alias non è un'istruzione init e pertanto non può essere utilizzata in contesti che consentono dichiarazioni di inizializzazione

// C++ 11.
for(using Foo = int; Foo{} != 0;) {}
//  ^^^^^^^^^^^^^^^ error: expected expression

// C++17 (initialization expressions in switch and if statements).
if (using Foo = int; true) { (void)Foo{}; }
//  ^^^^^^^^^^^^^^^ error: expected expression

switch(using Foo = int; 0) { case 0: (void)Foo{}; }
//     ^^^^^^^^^^^^^^^ error: expected expression

// C++20 (range-based for loop initialization statements).
std::vector<int> v{1, 2, 3};
for(using Foo = int; Foo f : v) { (void)f; }
//  ^^^^^^^^^^^^^^^ error: expected expression
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.