web-dev-qa-db-fra.com

Typedefs internes en C ++ - bon style ou mauvais style?

Quelque chose que je me suis souvent trouvé à faire dernièrement consiste à déclarer des modifications de type relatives à une classe particulière à l'intérieur de cette classe, c'est-à-dire.

class Lorem
{
    typedef boost::shared_ptr<Lorem> ptr;
    typedef std::vector<Lorem::ptr>  vector;

//
// ...
//
};

Ces types sont ensuite utilisés ailleurs dans le code:

Lorem::vector lorems;
Lorem::ptr    lorem( new Lorem() );

lorems.Push_back( lorem );

Les raisons qui me plaisent:

  • Il réduit le bruit introduit par les modèles de classe, std::vector<Lorem> devient Lorem::vector, etc.
  • Cela sert de déclaration d'intention - dans l'exemple ci-dessus, la classe Lorem est censée être comptée par le biais de boost::shared_ptr et stocké dans un vecteur.
  • Cela permet à l’implémentation de changer - c’est-à-dire que si Lorem devait être changé, le décompte de références de manière intrusive (via boost::intrusive_ptr) à un stade ultérieur, cela aurait un impact minimal sur le code.
  • Je pense que cela a l'air plus joli et qu'il est sans doute plus facile à lire.

Raisons pour lesquelles je n'aime pas ça:

  • Il y a parfois des problèmes avec les dépendances - si vous voulez intégrer, par exemple, un Lorem::vector au sein d’une autre classe mais n’a besoin (ou ne souhaite) que de transmettre la déclaration de Lorem (par opposition à l’introduction d’une dépendance sur son fichier d’en-tête), vous devrez alors utiliser les types explicites (par exemple, boost::shared_ptr<Lorem> plutôt que Lorem::ptr), ce qui est un peu incohérent.
  • Cela peut ne pas être très courant, et donc plus difficile à comprendre?

J'essaie d'être objectif avec mon style de codage, il serait donc bon d'obtenir d'autres opinions sur celui-ci afin de pouvoir disséquer un peu ma pensée.

167
Will Baker

Je pense que c'est un excellent style, et je l'utilise moi-même. Il est toujours préférable de limiter autant que possible la portée des noms, et l'utilisation de classes est le meilleur moyen de le faire en C++. Par exemple, la bibliothèque standard C++ utilise beaucoup de ressources typées au sein des classes.

142
anon

Cela sert de déclaration d'intention - dans l'exemple ci-dessus, la classe Lorem est destinée à être comptée de référence via boost :: shared_ptr et stockée dans un vecteur.

C'est exactement ce que ça fait pas faire.

Si je vois "Foo :: Ptr" dans le code, je ne sais absolument pas si c'est un shared_ptr ou un Foo * (STL a :: pointer typedefs qui sont T *, rappelez-vous) ou peu importe. Esp. S'il s'agit d'un pointeur partagé, je ne fournis pas du tout de texte, mais conserve l'utilisation explicitée de shared_ptr dans le code.

En fait, je n’utilise presque jamais de types de caractères en dehors de la métaprogrammation des modèles.

La STL fait ce genre de chose tout le temps

La conception STL avec des concepts définis en termes de fonctions membres et de typedefs imbriquées est un cul-de-sac historique, les bibliothèques de modèles modernes utilisent des fonctions libres et des classes de traits (cf. Boost.Graph), car elles n'excluent pas les types intégrés. modéliser le concept et parce qu'il facilite l'adaptation de types qui n'ont pas été conçus avec les concepts des bibliothèques de modèles donnés.

N'utilisez pas le TSL comme une raison pour faire les mêmes erreurs.

13
Marc Mutz - mmutz

Les typedefs sont ceux que conception et traits basés sur des règles construits en C++, la puissance de la programmation générique en C++ provient donc des typedefs eux-mêmes.

9
Özgür

Les typdefs sont vraiment bon style. Et toutes vos "raisons que j'aime" sont bonnes et correctes.

Des problèmes que tu as avec ça. Eh bien, la déclaration anticipée n’est pas un saint graal. Vous pouvez simplement concevoir votre code pour éviter les dépendances à plusieurs niveaux.

Vous pouvez déplacer typedef en dehors de la classe mais Class :: ptr est tellement plus joli que ClassPtr que je ne le fais pas. C'est comme avec les espaces de noms, comme pour moi - les choses restent connectées dans la portée.

Parfois j'ai fait

Trait<Loren>::ptr
Trait<Loren>::collection
Trait<Loren>::map

Et cela peut être par défaut pour toutes les classes de domaine et avec une spécialisation pour certaines.

8
Mykola Golubyev

Un autre vote pour que ce soit une bonne idée. J'ai commencé à faire cela en écrivant une simulation qui devait être efficace, à la fois dans le temps et dans l'espace. Tous les types de valeur avaient une typedef Ptr qui commençait par un pointeur partagé renforcé. J'ai ensuite fait du profilage et en ai transformé certains en un pointeur intrusif sans avoir à changer le code dans lequel ces objets étaient utilisés.

Notez que cela ne fonctionne que lorsque vous savez où les classes vont être utilisées et que toutes les utilisations ont les mêmes exigences. Je n'utiliserais pas cela dans le code de la bibliothèque, par exemple, car vous ne pouvez pas savoir lors de l'écriture de la bibliothèque le contexte dans lequel elle sera utilisée.

3
KeithB

La STL fait ce genre de chose tout le temps - les typedefs font partie de l'interface de nombreuses classes de la STL.

reference
iterator
size_type
value_type
etc...

sont toutes les typedefs qui font partie de l'interface pour différentes classes de modèles STL.

3
Michael Burr

Actuellement, je travaille sur du code, qui utilise intensivement ce type de typedef. Jusqu'ici, c'est bien.

Mais j’ai remarqué qu’il existe assez souvent des types de caractères itératifs, que les définitions sont réparties entre plusieurs classes et que vous ne savez jamais vraiment à quel type vous avez affaire. Ma tâche est de résumer la taille de certaines structures de données complexes cachées derrière ces typedefs - je ne peux donc pas compter sur les interfaces existantes. En combinaison avec trois à six niveaux d'espaces de noms imbriqués, cela crée de la confusion.

Donc, avant de les utiliser, il y a quelques points à considérer

  • Est-ce que quelqu'un d'autre a besoin de ces typedefs? La classe est-elle beaucoup utilisée par d'autres classes?
  • Est-ce que je raccourcis l'usage ou cache la classe? (En cas de dissimulation, vous pouvez également penser aux interfaces.)
  • Est-ce que d'autres personnes travaillent avec le code? Comment font-ils? Vont-ils penser que c'est plus facile ou vont-ils devenir confus?
2
Bodo

Je recommande de déplacer ces types de caractères en dehors de la classe. De cette façon, vous supprimez la dépendance directe sur les classes de pointeurs et de vecteurs partagés et vous pouvez les inclure uniquement lorsque cela est nécessaire. À moins que vous n'utilisiez ces types dans votre implémentation de classe, j'estime qu'ils ne devraient pas être des typedefs internes.

Les raisons qui vous plaisent sont toujours les mêmes, car elles sont résolues par le repliement de type via typedef, et non par leur déclaration dans votre classe.

1
Cătălin Pitiș

Lorsque la typedef est utilisée uniquement dans la classe elle-même (c'est-à-dire qu'elle est déclarée privée), je pense que c'est une bonne idée. Cependant, pour les raisons que vous donnez, je ne l'emploierais pas si le typedef doit être connu en dehors de la classe. Dans ce cas, je recommande de les déplacer en dehors de la classe.

0
Stefan Rådström