Was ist der Unterschied zwischen 'typedef' und 'using' in C ++ 11?

905

Ich weiß, dass wir in C ++ 11 jetzt Typalias usingschreiben können, wie typedefs:

typedef int MyInt;

Ist nach meinem Verständnis gleichbedeutend mit:

using MyInt = int;

Und diese neue Syntax entstand aus dem Bestreben heraus, " template typedef" auszudrücken :

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

Gibt es bei den ersten beiden Beispielen ohne Vorlage noch andere subtile Unterschiede im Standard? Zum Beispiel führen typedefwir Aliasing auf "schwache" Weise durch. Das heißt, es wird kein neuer Typ erstellt, sondern nur ein neuer Name (Konvertierungen sind zwischen diesen Namen implizit).

Ist es dasselbe mit usingoder generiert es einen neuen Typ? Gibt es Unterschiede?

Klaim
quelle
215
Ich persönlich bevorzuge die neue Syntax, da sie der regulären Variablenzuweisung viel ähnlicher ist und die Lesbarkeit verbessert. Bevorzugen Sie zum Beispiel typedef void (&MyFunc)(int,int);oder using MyFunc = void(int,int);?
Matthieu M.
13
Ich stimme voll und ganz zu, ich verwende jetzt nur die neue Syntax. Deshalb habe ich gefragt, ob es wirklich keinen Unterschied gibt.
Klaim
80
@MatthieuM. diese beiden sind übrigens unterschiedlich. Es sollte sein typedef void MyFunc(int,int);(was eigentlich nicht so schlecht aussieht), oderusing MyFunc = void(&)(int,int);
R. Martinho Fernandes
5
@ R.MartinhoFernandes warum brauchst du (&) in using MyFunc = void(&)(int,int);? Bedeutet das, dass es sich MyFuncum einen Verweis auf eine Funktion handelt? Was ist, wenn Sie das & weglassen ?
Rich
7
Ja, es ist eine Funktionsreferenz. Es ist gleichbedeutend mit typedef void (&MyFunc)(int,int);. Wenn Sie das weglassen, ist &es gleichbedeutend mittypedef void MyFunc(int,int);
R. Martinho Fernandes

Antworten:

585

Sie entsprechen dem Standard (Schwerpunkt Mine) (7.1.3.2):

Ein typedef-Name kann auch durch eine Alias-Deklaration eingeführt werden. Der Bezeichner nach dem Schlüsselwort using wird zu einem typedef-Namen, und der optionale Attributbezeichner-seq nach dem Bezeichner gehört zu diesem typedef-Namen. Es hat dieselbe Semantik, als ob es vom typedef-Spezifizierer eingeführt worden wäre. Insbesondere definiert es keinen neuen Typ und darf nicht in der Typ-ID erscheinen.

Jesse Gut
quelle
24
Aus der Antwort geht hervor, dass das usingSchlüsselwort eine Obermenge von ist typedef. Dann wird typdefin Zukunft veraltet sein?
Iammilind
49
Eine Abwertung bedeutet nicht unbedingt die Absicht zu entfernen - es ist lediglich eine sehr starke Empfehlung, ein anderes Mittel zu bevorzugen.
Iron Savior
18
Aber dann frage ich mich, warum sie nicht einfach zugelassen haben, dass typedef als Vorlage verwendet wird. Ich erinnere mich, dass ich irgendwo gelesen habe, dass sie die usingSyntax genau deshalb eingeführt haben , weil die typedefSemantik mit Vorlagen nicht gut funktioniert hat. Was irgendwie der Tatsache widerspricht, dass usinggenau dieselbe Semantik definiert ist.
Celtschk
12
@ Celtschk: Der Grund wird im Vorschlag angesprochen n1489. Ein Vorlagenalias ist kein Alias ​​für einen Typ, sondern ein Alias ​​für eine Gruppe von Vorlagen. Um zwischen typedefdem empfundenen Bedürfnis nach neuer Syntax zu unterscheiden. Beachten Sie auch, dass sich die Frage des OP auf den Unterschied zwischen Nicht-Vorlagenversionen bezieht.
Jesse Good
4
Warum wurde diese Redundanz eingeführt? 2 Syntaxen für den gleichen Zweck. Und ich sehe nie, dass typdefich jemals veraltet bin .
Benoît
237

Sie sind weitgehend gleich, außer dass:

Die Alias-Deklaration ist mit Vorlagen kompatibel, das C-Format typedef jedoch nicht.

Zhongming Qu
quelle
29
Besonders gut gefällt mir die Einfachheit der Antwort und der Hinweis auf den Ursprung des Schriftsatzes.
G24l
1
@ g24l du meinst typedef ... wahrscheinlich
Marc.2377
Was ist der Unterschied zwischen C und C ++, typedefwenn ich fragen darf?
McSinyx
196

Die Verwendung der Syntax hat einen Vorteil, wenn sie in Vorlagen verwendet wird. Wenn Sie die Typabstraktion benötigen, aber auch den Vorlagenparameter beibehalten müssen, damit er in Zukunft angegeben werden kann. Sie sollten so etwas schreiben.

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; }

Die Verwendung der Syntax vereinfacht diesen Anwendungsfall.

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

my_type<int> variable;
template <typename U> struct baz { my_type<U> _var_member; }
4xy
quelle
35
Ich habe dies jedoch bereits in der Frage erwähnt. Meine Frage ist, wenn Sie keine Vorlage verwenden, gibt es einen Unterschied zu typedef. Wie zum Beispiel, wenn Sie 'Foo foo {init_value};' Anstelle von 'Foo foo (init_value)' sollen beide dasselbe tun, aber nicht genau dieselben Regeln einhalten. Also habe ich mich gefragt, ob es einen ähnlichen versteckten Unterschied bei der Verwendung von / typedef gibt.
Klaim
24

Sie sind im Wesentlichen gleich, usingbieten aber, alias templateswas sehr nützlich ist. Ein gutes Beispiel, das ich finden könnte, ist wie folgt:

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

Also können wir std::add_const_t<T>statt verwendentypename std::add_const<T>::type

Validus Oculus
quelle
Soweit ich weiß, ist es undefiniertes Verhalten, irgendetwas in den
Standard-
5
@someonewithpc Ich habe nichts hinzugefügt, es existiert bereits, ich habe nur ein Beispiel für die Verwendung von Typnamen gezeigt. Bitte überprüfen Sie en.cppreference.com/w/cpp/types/add_cv
Validus Oculus
9

Ich weiß, dass das Originalposter eine großartige Antwort hat, aber für jeden, der über diesen Thread stolpert, wie ich, gibt es einen wichtigen Hinweis aus dem Vorschlag , der meiner Meinung nach der Diskussion hier etwas Wertvolles hinzufügt, insbesondere zu Bedenken in den Kommentaren darüber, ob das typedefSchlüsselwort lautet wird in Zukunft als veraltet markiert oder als überflüssig / alt entfernt entfernt:

Es wurde vorgeschlagen, das Schlüsselwort typedef ... (erneut) zu verwenden, um Vorlagen-Aliase einzuführen:

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

Diese Notation hat den Vorteil, dass ein Schlüsselwort verwendet wird, von dem bereits bekannt ist, dass es einen Typalias einführt. Es werden jedoch auch einige Nachteile angezeigt, darunter die Verwirrung, ein Schlüsselwort zu verwenden, von dem bekannt ist, dass es einen Alias ​​für einen Typnamen in einem Kontext einführt, in dem der Alias ​​keinen Typ, sondern eine Vorlage bezeichnet; Vecist kein Alias ​​für einen Typ und sollte nicht für einen typedef-Namen verwendet werden. Der Name Vecist ein Name für die Familie std::vector<•, MyAllocator<•> >- wobei das Aufzählungszeichen ein Platzhalter für einen Typnamen ist. Infolgedessen schlagen wir nicht die Syntax „typedef“ vor. Andererseits den Satz

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

kann gelesen / interpretiert werden als: von nun an werde ich Vec<T>als Synonym für verwendenstd::vector<T, MyAllocator<T> > . Mit dieser Lesart erscheint die neue Syntax für Aliasing ziemlich logisch.

Für mich bedeutet dies, dass das typedefSchlüsselwort in C ++ weiterhin unterstützt wird, da es den Code immer noch lesbarer und verständlicher machen kann .

Das Aktualisieren des usingSchlüsselworts erfolgte speziell für Vorlagen und (wie in der akzeptierten Antwort ausgeführt), wenn Sie mit Nicht-Vorlagen arbeiten usingund typedefmechanisch identisch sind. Die Wahl liegt also ganz im Sinne des Lesers und der Absichtskommunikation .

RoboticForest
quelle
2

Beide Schlüsselwörter sind gleichwertig, es gibt jedoch einige Einschränkungen. Zum einen ist das Deklarieren eines Funktionszeigers mit using T = int (*)(int, int);klarer als mit typedef int (*T)(int, int);. Zweitens ist das Template-Alias-Formular mit nicht möglich typedef. Drittens würde das Offenlegen der C-API typedefin öffentlichen Headern erforderlich sein .

Marski
quelle
0

Typedef-Deklarationen können als Initialisierungsanweisungen verwendet werden, während Alias-Deklarationen nicht verwendet werden können

Gibt es bei den ersten beiden Beispielen ohne Vorlage noch andere subtile Unterschiede im Standard?

Obwohl es sich um einen Eckfall handelt , ist eine typedef-Deklaration eine init-Anweisung und kann daher in Kontexten verwendet werden, die Initialisierungsanweisungen ermöglichen

// 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

während eine Alias-Deklaration ist nicht eine init-Anweisung , und somit kann nicht in einem Kontext verwendet werden , die ermöglicht Initialisierungsanweisungen

// 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
dfri
quelle