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

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

     

Et, depuis C ++ 11, également dans

   

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 la suivante:

Lequel des en-têtes ci-dessus dois-je inclure pour utiliser size_t ? Est-ce que ça compte du tout?

En supposant que je voulais minimiser les fonctions et les types que cstddef avec cstddef car il ne déclare aucune fonction et ne déclare que 6 types. Les autres se concentrent sur des domaines particuliers (chaînes, temps, entrées-sorties) qui peuvent ne pas vous intéresser.

Notez que cstddef ne garantit que la définition de std::size_t , c’est-à-dire en définissant size_t dans namespace std , bien qu’il puisse également fournir ce nom dans l’espace de noms global (effectivement, plain size_t ).

En revanche, stddef.h (qui est également un en-tête disponible dans C) garantit la définition de size_t dans l’espace de noms global et peut également fournir std::size_t .

En fait, le synopsis (inclus dans le standard C ++) de plusieurs en-têtes inclut notamment size_t tandis que d’autres en-têtes définissent le type size_t (basé sur le standard C car les en-têtes sont que des en-têtes ISO C le retrait de size_t n’est pas indiqué).

Le standard C ++ fait cependant référence à pour la définition de std::size_t

  • dans 18.2 Types ,
  • dans 5.3.3 Sizeof ,
  • dans 3.7.4.2 Fonctions de dissortingbution (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 parce que que des types et pas de fonctions, je m’en tiendrai à cet en-tête pour rendre std::size_t disponible.


Notez quelques choses:

  1. Le type de std::size_t peut être obtenu en utilisant decltype sans inclure d’en-tête

    Si vous prévoyez d’introduire un typedef dans votre code (c.-à-d. Que vous écrivez un conteneur et que vous voulez fournir un typedef size_type ), vous pouvez utiliser les opérateurs sizeof , sizeof... ou alignof pour définir votre type sans inclure d’en-tête. du tout puisque ces opérateurs retournent std::size_t par définition standard et que vous pouvez utiliser decltype :

     using size_type = decltype(alignof(char)); 
  2. std::size_t n’est pas en soi globalement visible bien que les fonctions avec les arguments std::size_t 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 , std ou std::size_t et

    se référant à std ou std::size_t est mal formé à moins que le nom ait été déclaré en incluant l’en-tête approprié.

  3. L’utilisateur ne peut pas redéfinir std::size_t bien qu’il soit possible d’avoir plusieurs typedefs se référant au même type dans le même espace de noms.

    Bien que l’occurrence de plusieurs définitions de size_t dans std soit parfaitement valide selon 7.1.3 / 3 , il n’est pas permis d’append des déclarations à namespace std conformément à 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 un espace de noms std, sauf indication contraire.

    Ajouter un typedef correct pour size_t à l’espace de noms ne viole pas le 7.1.3 mais viole le 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 (sauf quelques cas de spécialisation de modèle où un typedef n’est pas une spécialisation de modèle). Extension de l’ namespace std

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 dans _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 

Vous pourriez faire sans 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, le standard C ++ nécessite:

Le résultat de sizeof et sizeof... est une constante de type std::size_t . [Remarque: std::size_t est défini dans l’en-tête standard (18.2). – note finale]

En d’autres termes, la norme exige:

 static_assert(std::is_same::value, "This never fails."); 

Notez également qu’il est parfaitement correct de faire cette déclaration typedef dans les espaces de noms global et std , à condition qu’elle corresponde à toutes les autres déclarations typedef du même nom-typedef (une erreur de compilation est émise sur les déclarations non correspondantes).

Ceci est dû au fait:

  • §7.1.3.1 Un nom-typedef n’introduit pas un nouveau type comme le font une déclaration de classe (9.1) ou une déclaration enum.

  • §7.1.3.3 Dans un champ donné, un spécificateur typedef peut être utilisé pour redéfinir le nom de tout type déclaré dans ce domaine pour faire référence au type auquel il se réfère déjà.


Pour les sceptiques en disant que cela constitue un ajout d’un nouveau type dans namespace std , et un tel acte est explicitement interdit par la norme, et ceci est UB et tout y est; Je dois dire que cette attitude revient à ignorer et à nier une meilleure compréhension des problèmes sous-jacents.

La norme interdit d’append de nouvelles déclarations et définitions dans namespace std car, ce faisant, l’utilisateur peut créer un gâchis dans la bibliothèque standard et lancer l’ensemble de sa tâche. Pour les rédacteurs standard, il était plus facile de laisser l’utilisateur se spécialiser et d’interdire toute autre action, au lieu d’interdire tout ce que l’utilisateur ne devrait pas faire et risquer de rater quelque chose d’important (et cette étape). Ils l’ont fait par le passé en exigeant qu’aucun conteneur standard ne soit instancié avec un type incomplet, alors qu’en fait, certains conteneurs pourraient le faire (voir The Standard Librarian: Conteneurs de types incomplets par Matthew H. Austern ):

… Finalement, tout semblait trop sombre et trop mal compris; le comité de normalisation ne pensait pas 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 rest de la bibliothèque standard.

… rétrospectivement, maintenant que la technologie est mieux comprise, cette décision semble toujours 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, cela serait difficile ou impossible. Il était fort probable que le premier test que nous avons essayé, en utilisant std::vector , soit l’un des cas les plus faciles.

Étant donné que les règles de langage exigent que std::size_t soit exactement decltype(sizeof(int)) , faire de namespace std { using size_t = decltype(sizeof(int)); } 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 de decltype et par conséquent, aucun moyen de déclarer le type de sizeof une déclaration simple sans impliquer beaucoup de modèles. size_t alias différents types sur différentes architectures cibles, cependant, ce ne serait pas une solution élégante d’append un nouveau type intégré uniquement pour le résultat de sizeof , et il n’y a pas de typedefs intégrés standard. Par conséquent, la solution la plus portable à l’époque consistait à placer l’alias de type size_t dans un en-tête spécifique et à le documenter.

En C ++ 11, il existe désormais un moyen d’écrire cette exigence exacte de la norme sous la forme d’une déclaration simple.