En C++ il y a une différence entre :
struct Foo { ... };
et :
typedef struct { ... } Foo;
En C++ il y a une différence entre :
struct Foo { ... };
et :
typedef struct { ... } Foo;
En C++, il n'y a qu'une différence subtile. C'est un vestige du C, dans lequel il y a une différence.
La norme du langage C ( C89 §3.1.2.3 , C99 §6.2.3 y C11 §6.2.3 ) impose des espaces de noms distincts pour différentes catégories d'identifiants, notamment identificateurs d'étiquettes (pour struct
/ union
/ enum
) y identifiants ordinaires (pour typedef
et autres identifiants).
Si tu viens de dire :
struct Foo { ... };
Foo x;
vous obtiendriez une erreur de compilation, car Foo
n'est défini que dans l'espace de noms des balises.
Vous devriez le déclarer comme :
struct Foo x;
Chaque fois que vous voulez faire référence à un Foo
tu devras toujours appeler ça un struct Foo
. Cela devient vite ennuyeux, alors vous pouvez ajouter une touche typedef
:
struct Foo { ... };
typedef struct Foo Foo;
Maintenant struct Foo
(dans l'espace de noms de la balise) et tout simplement Foo
(dans l'espace de noms des identificateurs ordinaires) font tous deux référence à la même chose, et vous pouvez librement déclarer des objets de type Foo
sans le struct
mot-clé.
La construction :
typedef struct Foo { ... } Foo;
est juste une abréviation pour la déclaration et typedef
.
Enfin,
typedef struct { ... } Foo;
déclare une structure anonyme et crée un typedef
pour lui. Ainsi, avec cette construction, il n'a pas de nom dans l'espace de noms des balises, seulement un nom dans l'espace de noms typedef. Cela signifie qu'il ne peut pas non plus être déclaré à l'avance. Si vous voulez faire une déclaration prospective, vous devez lui donner un nom dans l'espace de noms des balises .
En C++, tous les struct
/ union
/ enum
/ class
Les déclarations agissent comme si elles étaient implicitement typedef
pour autant que le nom ne soit pas caché par une autre déclaration portant le même nom. Voir Réponse de Michael Burr pour tous les détails.
Bien que ce que vous dites soit vrai, à ma connaissance, l'énoncé "typedef struct { ... } Foo ;" crée un alias pour une structure non nommée.
Bien vu, il y a une différence subtile entre "typedef struct Foo { ... } Foo ;" et "typedef struct { ... } Foo ;".
En C, les balises struct, union et enumeration partagent un seul espace de noms, plutôt que d'en utiliser deux (struct et union) comme indiqué ci-dessus ; l'espace de noms référencé pour les noms typedef est en effet séparé. Cela signifie que vous ne pouvez pas avoir à la fois 'union x { ... } ;" et "struct x { ... };' dans une seule portée.
En cet article du DDJ Dan Saks explique une petite zone où les bogues peuvent se faufiler si vous ne typisez pas vos structures (et vos classes !):
Si vous voulez, vous pouvez imaginer que le C++ génère un typedef pour chaque balise tel que
typedef class string string;
Malheureusement, ce n'est pas tout à fait exact. J'aimerais que ce soit aussi simple, mais ça ne l'est pas. Le C++ ne peut pas générer de tels typedefs pour les structs, unions, ou enums sans introduire des incompatibilités avec le C.
Par exemple, supposons qu'un programme C déclare à la fois une fonction et une structure nommée status :
int status(); struct status;
Encore une fois, c'est peut-être une mauvaise pratique, mais mais c'est du C. Dans ce programme, status (en soi) fait lui-même) fait référence à la fonction ; struct status fait référence au type.
Si le C++ génère automatiquement typedefs pour les tags, alors quand vous compilé ce programme en C++, le compilateur aurait généré :
typedef struct status status;
Malheureusement, ce nom de type serait conflit avec le nom de la fonction, et le programme ne serait pas compilé. C'est pourquoi pourquoi le C++ ne peut pas simplement générer un typedef pour chaque balise.
En C++, les balises agissent exactement comme les noms typedef sauf qu'un programme peut déclarer un objet, une fonction ou un énumérateur avec le même nom et la même même portée qu'une balise. Dans ce cas, le nom de l'objet, de la fonction ou de l'énumérateur cache le nom de la balise. Le programme peut faire référence au nom de la balise uniquement en utilisant le mot-clé class, struct, union, ou enum (selon le cas) devant le nom de la balise nom de balise. Un nom de type composé de un de ces mots-clés suivi d'une balise est un spécificateur de type élaboré. Par exemple, struct status et enum month sont des spécificateurs de type élaborés.
Ainsi, un programme C qui contient les deux :
int status(); struct status;
se comporte de la même manière lorsqu'il est compilé en C++. Le nom status seul fait référence à la fonction. Le programme peut se référer au type uniquement en utilisant le spécificateur de type élaboré struct statut.
Alors comment cela permet-il aux bugs de se faufiler dans les programmes ? Considérons le programme dans Liste 1 . Ce programme définit une classe foo avec un constructeur par défaut, et un opérateur de conversion qui convertit un objet foo en char const *. L'expression
p = foo();
dans main devrait construire un objet foo et appliquer l'opérateur de conversion. Le site déclaration de sortie suivante
cout << p << '\n';
devrait afficher la classe foo, mais ce n'est mais ce n'est pas le cas. Il affiche la fonction foo.
Ce résultat surprenant se produit parce que le programme inclut l'en-tête lib.h indiqué dans Liste 2 . Cet en-tête définit une fonction également nommée foo. Le site fonction foo cache le nom de la classe foo, de sorte que la référence à foo dans main fait référence à la fonction, et non à la classe. main ne peut faire référence à la classe qu'en en utilisant un spécificateur de type élaboré, comme dans
p = class foo();
La façon d'éviter une telle confusion dans tout le programme est d'ajouter le typedef suivant pour le nom de la classe foo :
typedef class foo foo;
immédiatement avant ou après la définition de la classe définition. Ce typedef provoque un conflit entre le nom de type foo et le nom de la fonction foo (de la bibliothèque) qui déclenchera une erreur de compilation.
Je ne connais personne qui écrit réellement ces typedefs comme une évidence. Cela demande beaucoup de discipline. Depuis l'incidence des erreurs telles que la dans Liste 1 est probablement assez petite, vous n'aurez probablement jamais à faire face à ce problème. Mais si une erreur dans votre logiciel peut causer des blessures corporelles, alors vous devez écrire les typedefs, peu importe même si l'erreur est peu probable.
Je ne peux pas imaginer pourquoi quelqu'un voudrait vouloir cacher un nom de classe avec un fonction ou un nom d'objet dans la même que la classe. Les règles de dissimulation en C étaient une erreur, et elles n'auraient pas être étendues aux classes en C++. En effet, vous pouvez corriger cette l'erreur, mais cela demande une discipline discipline de programmation et des efforts supplémentaires qui qui ne devraient pas être nécessaires.
Au cas où vous essayez "class foo()" et que cela échoue : Dans le C++ ISO, "class foo()" est une construction illégale (l'article a été écrit en 97, avant la normalisation, semble-t-il). Vous pouvez mettre "typedef class foo foo ;" dans main, puis vous pouvez dire "foo() ;" (car alors, le nom du typedef est lexicalement plus proche que le nom de la fonction). Syntaxiquement, dans T(), T doit être un spécificateur de type simple. Les spécificateurs de type élaborés ne sont pas autorisés. C'est quand même une bonne réponse, bien sûr.
Une autre différence importante : typedef
ne peuvent pas être déclarés à l'avance. Ainsi, pour le typedef
vous devez #include
le fichier contenant le typedef
c'est-à-dire tout ce qui #include
s votre .h
inclut également ce fichier, qu'il en ait besoin ou non, et ainsi de suite. Cela peut certainement avoir un impact sur vos temps de construction sur les grands projets.
Sans le typedef
dans certains cas, vous pouvez simplement ajouter une déclaration directe de struct Foo;
en haut de votre .h
et seulement #include
la définition de la structure dans votre .cpp
fichier.
Pourquoi l'exposition de la définition d'une structure a-t-elle un impact sur le temps de construction ? Le compilateur effectue-t-il une vérification supplémentaire même s'il n'a pas besoin de le faire (étant donné l'option typedef, afin que le compilateur connaisse la définition) lorsqu'il voit quelque chose comme Foo* nextFoo ; ?
Ce n'est pas vraiment une vérification supplémentaire, c'est juste plus de code que le compilateur doit gérer. Pour chaque fichier cpp qui rencontre ce typedef quelque part dans sa chaîne d'inclusion, il compile le typedef. Dans les projets plus importants, le fichier .h contenant le typedef peut facilement être compilé des centaines de fois, bien que les en-têtes précompilés aident beaucoup. Si vous pouvez vous en sortir en utilisant une déclaration directe, il est alors plus facile de limiter l'inclusion du fichier .h contenant la spécification complète de la structure au seul code qui s'y intéresse vraiment, et donc le fichier d'inclusion correspondant est compilé moins souvent.
Veuillez @ le commentateur précédent (autre que le propriétaire de l'article). J'ai failli manquer votre réponse. Mais merci pour l'info.
Il y a es une différence, mais subtile. Voyez-le de cette façon : struct Foo
introduit un nouveau type. La seconde crée un alias appelé Foo (et non un nouveau type) pour un objet sans nom, le struct
type.
7.1.3 Le spécificateur typedef
1 [...]
Un nom déclaré avec le spécificateur typedef devient un nom typedef. Dans le cadre de sa déclaration, un est syntaxiquement équivalent à un mot-clé et nomme le type associé à l'identificateur de la manière décrite dans la clause 1. de la manière décrite dans la clause 8. Un nom de type est donc un synonyme d'un autre type. Un nom de type n'introduit pas un nouveau type de la même manière qu'une déclaration de classe (9.1) ou d'enum.
8 Si la déclaration typedef définit une classe (ou un enum) sans nom, le premier nom typedef déclaré par la déclaration comme étant le type de la classe (ou de l'enum) est le premier nom typedef. déclarée par la déclaration comme étant ce type de classe (ou d'énumération) est utilisée pour désigner le type de classe (ou d'énumération) à des fins de liaison uniquement (3.5). uniquement à des fins de liaison (3.5). [ Exemple :
typedef struct { } *ps, S; // S is the class name for linkage purposes
Ainsi, un typedef toujours est utilisé comme un substitut/synonyme d'un autre type.
Vous ne pouvez pas utiliser la déclaration forward avec la structure typedef.
La structure elle-même est un type anonyme, donc vous n'avez pas de nom réel à déclarer.
typedef struct{
int one;
int two;
}myStruct;
Une déclaration anticipée comme celle-ci ne fonctionnera pas :
struct myStruct; //forward declaration fails
void blah(myStruct* pStruct);
//error C2371: 'myStruct' : redefinition; different basic types
Je n'ai pas la deuxième erreur pour le prototype de fonction. Pourquoi est-ce que ça dit "redéfinition ; différents types de base" ? Le compilateur n'a pas besoin de savoir à quoi ressemble la définition de myStruct, non ? Peu importe le morceau de code (le typedef ou le forward declaration), myStruct dénote un type struct, n'est-ce pas ?
@Rich Il se plaint qu'il y a un conflit de noms. Il y a une déclaration forward qui dit "chercher une structure appelée myStruct" et ensuite il y a le typedef qui renomme une structure sans nom en "myStruct".
Vous voulez dire mettre la déclaration typedef et forward dans le même fichier ? Je l'ai fait, et gcc l'a compilé sans problème. myStruct est correctement interprété comme la structure sans nom. Tag myStruct
vit dans l'espace de noms des tags et est typedef_ed myStruct
vit dans l'espace de nom normal où se trouvent d'autres identifiants comme le nom de la fonction, les noms de variables locales. Il ne devrait donc pas y avoir de conflit. Je peux vous montrer mon code si vous doutez qu'il y ait une erreur.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
0 votes
Question et réponse connexes : différence entre using et typedef en C++11
2 votes
Voir aussi la question connexe sur C