79 votes

Quand devrais-je utiliser typedef en C++ ?

En programmation C++ (MFC), je n'ai jamais ressenti le besoin d'utiliser typedef, donc je ne sais pas vraiment à quoi ça sert. Où devrais-je l'utiliser? Y a-t-il des situations réelles où l'utilisation de typedef est préférée? Ou est-ce vraiment un mot-clé spécifique à C?

0 votes

Par curiosité, qu'est-ce que le MFC ?

1 votes

91voto

j_random_hacker Points 28473

Méta-programmation avec modèles

typedef est nécessaire pour de nombreuses tâches de méta-programmation avec modèles -- chaque fois qu'une classe est traitée comme une "fonction de type de temps de compilation", un typedef est utilisé comme une "valeur de type de temps de compilation" pour obtenir le type résultant. Par exemple, considérons une simple métafonction pour convertir un type de pointeur en son type de base :

template
struct strip_pointer_from;

template
struct strip_pointer_from {   // Spécialisation partielle pour les types de pointeur
    typedef T type;
};

Exemple : l'expression de type strip_pointer_from::type évalue à double. Notez que la méta-programmation avec modèles n'est pas couramment utilisée en dehors du développement de bibliothèques.

Simplification des types de pointeur de fonction

typedef est utile pour donner un alias court et concis à des types de pointeurs de fonction compliqués :

typedef int (*my_callback_function_type)(int, double, std::string);

void RegisterCallback(my_callback_function_type fn) {
    ...
}

2 votes

Nécessaire? Vous voulez donner un exemple? Je ne peux pas penser à des cas où ce serait nécessaire.

13 votes

Pour C++11, l'ajout de la syntaxe "using a = b" laisse agréablement le mot-clé "typedef" pour la plupart des souvenirs, car typedef était toujours déroutant à l'envers et incohérent avec #define (maintenant je n'inverse jamais accidentellement les deux car c'est la même chose que l'ordre d'assignation des variables)

40voto

Jason Punyon Points 21244

Dans le livre de Bjarne, il déclare que vous pouvez utiliser typedef pour résoudre les problèmes de portabilité entre des systèmes ayant des tailles d'entiers différentes. (c'est une paraphrase)

Sur une machine où sizeof(int) est de 4, vous pouvez

typedef int int32;

Utilisez ensuite int32 partout dans votre code. Lorsque vous passez à une implémentation de C++ où sizeof(int) est de 2, vous pouvez simplement changer le typdef

typedef long int32;

et votre programme fonctionnera toujours sur la nouvelle implémentation.

13 votes

Naturellement, vous utiliseriez le uint32_t de , n'est-ce pas? :)

0 votes

Et seulement dans ces cas, généralement rares, où vous avez besoin exactement de 32 bits.

7 votes

@KeithB: Je pense que la rareté dépend du type de développement que vous effectuez. Les développeurs de systèmes embarqués et ceux qui traitent fréquemment les formats de fichiers sont deux cas auxquels je pense quand vous aurez souvent besoin de connaître des tailles exactes.

23voto

yesraaj Points 12759

Utilisez avec un pointeur de fonction

Masquer les déclarations de pointeurs de fonction avec un typedef

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

Seuls quelques programmeurs peuvent dire que p est un "tableau de 10 pointeurs vers une fonction renvoyant void et prenant un pointeur vers une autre fonction qui renvoie void et ne prend aucun argument". La syntaxe lourde est presque indéchiffrable. Cependant, vous pouvez considérablement simplifier en utilisant des déclarations de typedef. Tout d'abord, déclarez un typedef pour "pointeur vers une fonction renvoyant void et ne prenant aucun argument" comme suit :

  typedef void (*pfv)();

Ensuite, déclarez un autre typedef pour "pointeur vers une fonction renvoyant void et prenant un pfv" basé sur le typedef que nous avons précédemment déclaré :

 typedef void (*pf_taking_pfv) (pfv);

Maintenant que nous avons créé le typedef pf_taking_pfv comme un synonyme du peu pratique "pointeur vers une fonction renvoyant void et prenant un pfv", déclarer un tableau de 10 de tels pointeurs est un jeu d'enfant :

  pf_taking_pfv p[10];

de

18voto

peterchen Points 21792

Just to provide some examples for the things said: STL containers.

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

Il n'est pas rare d'utiliser aussi des typedefs comme

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

Un autre exemple : utiliser des pointeurs partagés :

class Froboz;
typedef boost::shared_ptr FrobozPtr;

[mise à jour] Selon le commentaire - où les placer ?

Le dernier exemple - l'utilisation de shared_ptr - est facile : ce sont du matériel de type en-tête réel - ou du moins un en-tête anticipé. Vous avez de toute façon besoin de la déclaration anticipée pour shared_ptr, et l'un de ses avantages déclarés est qu'il est sûr de l'utiliser avec une déclaration anticipée.

Dit d'une autre manière : s'il y a un shared_ptr, vous devriez probablement utiliser le type uniquement à travers un shared_ptr, donc séparer les déclarations n'a pas beaucoup de sens.

(Oui, xyzfwd.h est pénible. Je ne les utiliserai que dans les points chauds - sachant que les points chauds sont difficiles à identifier. Blâmez le modèle de compilation+liaison C++...)

Les typedefs de conteneurs que j'utilise généralement là où la variable de conteneur est déclarée - par exemple localement pour une variable locale, en tant que membres de classe lorsque l'instance de conteneur réelle est un membre de classe. Cela fonctionne bien si le type de conteneur réel est un détail d'implémentation - ne causant aucune dépendance supplémentaire.

S'ils font partie d'une interface particulière, ils sont déclarés conjointement avec l'interface avec laquelle ils sont utilisés, par exemple

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

Cela devient problématique lorsque le type est un élément de liaison entre différentes interfaces - c'est-à-dire que le même type est nécessaire par plusieurs en-têtes. Quelques solutions :

  • le déclarer conjointement avec le type contenu (adapté aux conteneurs fréquemment utilisés pour ce type)
  • les déplacer dans un en-tête séparé
  • les déplacer dans un en-tête séparé, et en faire une classe de données où le conteneur réel est à nouveau un détail d'implémentation

Je suis d'accord que les deux dernières options ne sont pas géniales, je ne les utiliserai que si je rencontre des problèmes (pas de manière proactive).

0 votes

Pouvez-vous discuter des bonnes pratiques pour les fichiers d'en-tête ? Les options semblent être de mettre le typedef dans Froboz.h, ce qui crée une dépendance d'en-tête et des temps de construction longs ; mettre les typedefs dans Frobozfwd.h (selon Effective C++), ce qui semble compliqué pour la maintenabilité (deux en-têtes pour tout) ; ou mettre les typedefs dans FroCommon.h, ce qui diminue la réutilisabilité. Y a-t-il une meilleure solution ?

1 votes

Merci. J'ai mis une version plus longue de cette question ici : stackoverflow.com/questions/2356548/…. Je crains d'être arrivé aux mêmes conclusions jusqu'à présent, à savoir qu'il n'y a pas vraiment de bonne réponse que vous puissiez utiliser de manière cohérente, ce qui signifie qu'il est difficile d'avoir une règle que tout le monde dans l'équipe peut suivre et sur laquelle compter. "Pour ce fichier d'en-tête, vous devez utiliser la version fwd, mais ceci vous incluez simplement le fichier d'en-tête de base, et ceci le truc connexe est défini ici dans common.h..." Comment quelqu'un peut-il écrire du C++ maintenable et réutilisable ? (ObjC m'a gâté... :D)

6voto

Emiliano Points 2165

Typedef est utile dans de nombreuses situations.

Fondamentalement, cela vous permet de créer un alias pour un type. Lorsque vous devez changer le type, le reste du code pourrait rester inchangé (cela dépend bien sûr du code). Par exemple, disons que vous voulez itérer sur un vecteur C++

vector v;

...

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

// Des trucs ici

}

À l'avenir, vous pourriez penser à changer le vecteur par une liste, en fonction du type d'opérations que vous devez effectuer dessus. Sans typedef, vous devriez changer TOUTES les occurrences de vecteur dans votre code. Mais si vous écrivez quelque chose comme ceci :

typedef vector my_vect;

my_vect v;

...

for(my_vect::const_iterator i = v->begin(); i != v.end(); i++) {

// Des trucs ici

}

Maintenant, vous n'avez qu'à changer une ligne de code (c'est-à-dire de "typedef vector my_vect" à "typedef list my_vect") et tout fonctionne.

typedef vous fait également gagner du temps lorsque vous avez des structures de données complexes qui sont très longues à écrire (et difficiles à lire)

1 votes

Ce n'est pas vraiment une bonne raison d'utiliser les typedef : vous devriez utiliser un type interface pour cela (Type de Données Abstrait, si vous préférez). C'est pourquoi vous avez besoin d'ajouter le 'dépend du code'. Ce devrait être le code qui dépend du type :)

0 votes

Et C++0x arrive! AWW-TO! AWW-TO! AWW-TO!

3 votes

@xtofl : Les typedefs et les types d'interface sont deux moyens valides de résoudre ce problème particulier. Les types d'interface sont plus généraux, mais ils sont aussi plus lourds. De plus, l'utilisation correcte des types d'interface implique que tous les appels seront virtuels -- un prix élevé pour l'avancement/déréférencement de l'itérateur.

Prograide.com

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.

Powered by:

X