Quel en-tête dois-je inclure pour `size_t`?

95

Selon cppreference.com size_t est défini dans plusieurs en-têtes, à savoir

<cstddef>
<cstdio>
<cstring>
<ctime>

Et, depuis C ++ 11, également dans

<cstdlib>
<cwchar> 

Tout d'abord, je me demande pourquoi c'est le cas. N'est-ce pas en contradiction avec le principe DRY ? Cependant, ma question est:

Lequel des en-têtes ci-dessus dois-je inclure pour utiliser size_t? Est-ce vraiment important?

idclev 463035818
la source
1
Ouvrez les fichiers d'en-tête correspondants et recherchez la définition.
i486
33
@ i486 - C'est un excellent moyen d'écrire du code non portable fragile!
Sean
3
@PanagiotisKanavos En-têtes C qui font partie de la bibliothèque standard C ++ et ne sont probablement pas dupliqués dans l'un de vos prétendus en-têtes 'true C ++'. Quel était votre point exactement?
underscore_d
14
J'ai toujours utilisé <cstddef>pourstd::size_t
Boiethios
4
@PanagiotisKanavos Bien sûr, c'est généralement un bon conseil, mais dans ce cas, cela ne semble pas pertinent - car il n'y a pas de remplacement C ++ pour std::size_t, et l'OP ne préconisait pas l'utilisation de fonctions C héritées, observant simplement la citation à propos du partage du typedef. Je doute que quiconque lisant ce fil soit induit en erreur en utilisant des types / fonctions hérités à cause de cela, mais si vous voulez être sûr qu'ils ne le font pas, alors assez bien!
underscore_d

Réponses:

90

En supposant que je voulais minimiser les fonctions et les types que j'importais, j'irais avec cstddefcar il ne déclare aucune fonction et ne déclare que 6 types. Les autres se concentrent sur des domaines particuliers (chaînes, temps, IO) qui peuvent ne pas vous intéresser.

Notez que cstddefseules les garanties de définirstd::size_t , c'est-à-dire de définir size_tdans l'espace de noms std, bien qu'il puisse également fournir ce nom dans l'espace de noms global (effectivement, simple size_t).

En revanche, stddef.h(qui est également un en-tête disponible en C) garantit de définir size_tdans l'espace de noms global, et peut également fournirstd::size_t .

Sean
la source
3
Y a-t-il une garantie que size_tde cstddefest le même et sera toujours le même que les autres? On dirait qu'il devrait y avoir un fichier d'en-tête commun avec des définitions communes comme size_t...
SnakeDoc
1
@SnakeDoc et comme par magie, une autre réponse ici a déjà observé exactement ce qui se passe, via un en-tête «interne».
underscore_d
5
@SnakeDoc Oui, et cet en-tête est cstddef.
user253751
2
@SnakeDoc, qui dit qu'ils définissent le leur? Tout ce que le standard dit, c'est qu'il sera défini après avoir inclus ces en-têtes, il ne dit pas qu'ils doivent tous le redéfinir. Ils pourraient tous inclure <cstddef>, ou ils pourraient tous inclure un en-tête interne qui définit simplement size_t.
Jonathan Wakely
1
La csttddefréponse est-elle une faute de frappe? Peut cstddef- être que cela veut dire?
Erik Sjölund
46

En fait, le synopsis (inclus dans la norme C ++) de plusieurs en-têtes inclut spécifiquement size_tainsi que d'autres en-têtes définissent le type size_t(basé sur la norme C car les en- <cX>têtes ne sont que des en- têtes ISO C <X.h>avec des changements notés où la suppression de size_tn'est pas indiquée).

Le standard C ++ fait cependant référence à <cstddef>la définition destd::size_t

  • dans 18.2 types ,
  • dans 5.3.3 Sizeof ,
  • dans 3.7.4.2 Fonctions de désallocation (qui se réfère à 18.2) et
  • dans 3.7.4.1 Fonctions d'allocation (se réfère également à 18.2).

Par conséquent et en raison du fait que <cstddef>n'introduit que des types et aucune fonction, je m'en tiendrai à cet en-tête pour le rendre std::size_tdisponible.


Notez quelques points:

  1. Le type de std::size_tpeut être obtenu en utilisant decltypesans inclure d'en-tête

    Si vous avez l' intention de présenter un typedef dans votre code de toute façon (parce que vous écrivez un conteneur et que vous voulez fournir un size_typetypedef) , vous pouvez utiliser les mondiaux sizeof, sizeof...ou les alignofopérateurs pour définir votre type sans inclure les en- têtes du tout depuis lces opérateurs retour std::size_tpar définition standard et vous pouvez utiliser decltypesur eux:

    using size_type = decltype(alignof(char));
  2. std::size_tn'est pas en soi globalement visible bien que les fonctions avec std::size_targuments le soient.

    Les fonctions d'allocation et de désallocation globales déclarées implicitement

    void* operator new(std::size_t);
    void* operator new[](std::size_t);
    void operator delete(void*);
    void operator delete[](void*);

    NE PAS introduire size_t, stdou std::size_tet

    se référant stdou std::size_test mal formé à moins que le nom n'ait été déclaré en incluant l'en-tête approprié.

  3. L'utilisateur ne peut pas redéfinir std::size_tbien qu'il soit possible d'avoir plusieurs typedefs faisant référence au même type dans le même espace de noms.

    Bien que l'occurrence de plusieurs définitions de size_twithin stdsoit parfaitement valide selon 7.1.3 / 3 , il n'est pas permis d'ajouter des déclarations à namespace stdselon 17.6.4.2.1 / 1 :

    Le comportement d'un programme C ++ n'est pas défini s'il ajoute des déclarations ou des définitions à l'espace de noms std ou à un espace de noms dans l'espace de noms std, sauf indication contraire.

    L'ajout d'un typedef pour size_tà l'espace de noms ne viole pas 7.1.3 mais viole 17.6.4.2.1 et conduit à un comportement indéfini.

    Clarification: essayez de ne pas mal interpréter 7.1.3 et n'ajoutez pas de déclarations ou de définitions std(à l'exception de quelques cas de spécialisation de modèle où un typedef n'est pas une spécialisation de modèle). Extension de lanamespace std

Pixelchimiste
la source
1
Vous manquez le fait qu'un typedef dupliqué n'introduit pas de nouveau type. Il ajoute simplement un typedef dupliqué, ce qui est parfaitement valide.
Maxim Egorushkin
@MaximEgorushkin: Je ne prétends pas que l'ajout d'un typedef redéfinissant à stdest invalide car les typedef en double sont illégaux. Je déclare que c'est illégal parce que vous ne pouvez tout simplement pas ajouter de définitions namespace std- peu importe si elles seraient légales.
Pixelchemist
Qu'est-ce qui pourrait potentiellement casser, étant donné tout ce que nous savons de toutes ces citations standard?
Maxim Egorushkin
12
@MaximEgorushkin: N'importe quoi. C'est ça le comportement indéfini, n'est-ce pas? Le fait que cela puisse fonctionner ou même le fait qu'il ne casse sur aucun compilateur arbitraire ne rend pas le comportement du programme défini selon la norme. Ou comme 'fredoverflow' l'a bien dit ici : "Le standard C ++ a le seul vote, point final ."
Pixelchemist
J'aimerais que vous utilisiez votre esprit critique. Qu'est-ce qui pourrait potentiellement casser?
Maxim Egorushkin
9

Tous les fichiers d'en-tête de bibliothèque standard ont la même définition; peu importe celui que vous incluez dans votre propre code. Sur mon ordinateur, j'ai la déclaration suivante au format _stddef.h. Ce fichier est inclus dans chaque fichier que vous avez répertorié.

/*
   Define the size_t type in the std namespace if in C++ or globally if in C.
   If we're in C++, make the _SIZE_T macro expand to std::size_t
*/

#if !defined(_SIZE_T) && !defined(_SIZE_T_DEFINED)
#  define _SIZE_T_DEFINED
#if defined(_WIN64)
   typedef unsigned __int64 size_t;
#else
   typedef unsigned int size_t;
#endif
#  if defined(__cplusplus)
#    define _SIZE_T std::size_t
#  else
#    define _SIZE_T size_t
#  endif
#endif
vll
la source
2
pas sûr, mais je pense que c'est important pour le temps de compilation, non?
idclev 463035818
@ tobi303 pas pour cette question spécifique. Oui, vous pouvez ajouter un en-tête plus grand que nécessaire, mais vous avez déjà ajouté un en-tête C dans un projet C ++. Pourquoi avez-vous besoin size_ten premier lieu?
Panagiotis Kanavos
Ce n'est pas une bonne idée d'utiliser le reniflement de macros OS pour définir size_t. Vous pouvez le définir de manière plus portable comme using size_t = decltype( sizeof( 42 ) ). Mais il n'y a pas besoin, car <stddef.h>a un coût presque nul.
Acclamations et hth. - Alf le
4

Vous pourriez vous passer d'un en-tête:

using size_t = decltype(sizeof(int));
using size_t = decltype(sizeof 1); //  The shortest is my favourite.
using size_t = decltype(sizeof "anything");

En effet, la norme C ++ requiert:

Le résultat de sizeofet sizeof...est une constante de type std::size_t. [Remarque: std::size_test défini dans l'en-tête standard <cstddef>(18.2). - note de fin]

En d'autres termes, la norme exige:

static_assert(std::is_same<decltype(sizeof(int)), std::size_t>::value,
              "This never fails.");

Notez également qu'il est parfaitement bien de faire cette typedefdéclaration dans l' stdespace de noms global et dans , tant qu'elle correspond à toutes les autres typedefdéclarations du même typedef-name (une erreur de compilation est émise sur les déclarations non correspondantes).

Ceci est dû au fait:

  • §7.1.3.1 Un typedef-name n'introduit pas de nouveau type comme le fait une déclaration de classe (9.1) ou une déclaration enum.

  • §7.1.3.3 Dans une portée non-classe donnée, un typedefspécificateur peut être utilisé pour redéfinir le nom de tout type déclaré dans cette portée pour faire référence au type auquel il fait déjà référence.


Aux sceptiques disant que cela constitue un ajout d'un nouveau type dans l'espace de noms std , et qu'un tel acte est explicitement interdit par la norme, et c'est UB et c'est tout là-dedans; Je dois dire que cette attitude revient à ignorer et à nier une compréhension plus profonde des problèmes sous-jacents.

Le standard interdit l'ajout de nouvelles déclarations et définitions dans l'espace de noms stdcar, ce faisant, l'utilisateur peut faire un désordre de la bibliothèque standard et lui tirer dessus toute sa jambe. Pour les rédacteurs standard, il était plus facile de laisser l'utilisateur se spécialiser dans certaines choses spécifiques et interdire de faire quoi que ce soit d'autre pour faire bonne mesure, plutôt que d'interdire chaque chose que l'utilisateur ne devrait pas faire et risquerait de manquer quelque chose d'important (et cette étape). Ils l'ont fait dans le passé en exigeant qu'aucun conteneur standard ne soit instancié avec un type incomplet, alors qu'en fait certains conteneurs pourraient bien le faire (voir The Standard Librarian: Containers of Incomplete Types par Matthew H.Austern ):

... En fin de compte, tout cela semblait trop trouble et trop mal compris; le comité de normalisation n'a pas pensé qu'il y avait d'autre choix que de dire que les conteneurs STL ne sont pas censés fonctionner avec des types incomplets. Pour faire bonne mesure, nous avons également appliqué cette interdiction au reste de la bibliothèque standard.

... Rétrospectivement, maintenant que la technologie est mieux comprise, cette décision semble encore fondamentalement juste. Oui, dans certains cas, il est possible d'implémenter certains des conteneurs standard afin qu'ils puissent être instanciés avec des types incomplets - mais il est également clair que dans d'autres cas, ce serait difficile, voire impossible. C'était surtout par hasard que le premier test que nous avons essayé, en utilisant std::vector, se soit avéré être l'un des cas faciles.

Étant donné que les règles linguistiques exigent std::size_td'être exactes decltype(sizeof(int)), faire namespace std { using size_t = decltype(sizeof(int)); }est l'une de ces choses qui ne cassent rien.

Avant C ++ 11, il n'y avait pas decltypeet donc aucun moyen de déclarer le type de sizeofrésultat en une seule instruction simple sans impliquer un grand nombre de modèles. size_talias différents types sur différentes architectures cibles, cependant, ce ne serait pas une solution élégante d'ajouter un nouveau type intégré uniquement pour le résultat de sizeof, et il n'y a pas de typedefs standard intégrés. Par conséquent, la solution la plus portable à l'époque était de mettre size_tun alias de type dans un en-tête spécifique et de le documenter.

En C ++ 11, il existe maintenant un moyen d'écrire cette exigence exacte de la norme comme une simple déclaration.

Maxim Egorushkin
la source
6
@Sean Ce que vous avez écrit n'a aucun sens.
Maxim Egorushkin
15
@MaximEgorushkin La moitié d'entre eux n'ont pas compris ce code ... cela fonctionne parfaitement. Cependant, je n'aime pas cette façon: il vaut mieux, à mon humble avis, inclure un en-tête et laisser le standard le définir.
Boiethios
9
Les gars, apprenez au moins la langue effrayante avant de voter pour des réponses parfaitement correctes.
Frédéric Hamidi
11
Tom a dit: "Il y a 6 en-têtes de bibliothèque standard définissant la même chose! C'est insensé! Nous avons besoin d'une et une seule définition de size_t !" Une minute plus tard, Mary a dit, "OMG! Il y a 7 définitions d' size_ten-têtes de bibliothèque standard et un en-tête de projet que Tom est en train de modifier! Il y en a probablement d'autres dans les bibliothèques tierces!" xkcd.com/927
6
Bien que ce soit une définition possible de size_t, cela ne répond pas à la vraie question du PO: c'est comme si j'avais demandé l'en-tête où FILEest déclaré et que vous suggéreriez d'écrire le mien.
edmz