Welchen Header soll ich für `size_t` einfügen?

95

Laut cppreference.com size_t ist in mehreren Headern nämlich definiert

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

Und seit C ++ 11 auch in

<cstdlib>
<cwchar> 

Zunächst frage ich mich, warum dies der Fall ist. Steht dies nicht im Widerspruch zum DRY- Prinzip? Meine Frage ist jedoch:

Welchen der oben genannten Header sollte ich einschließen, um ihn zu verwenden size_t? Ist das überhaupt wichtig?

idclev 463035818
quelle
1
Öffnen Sie die entsprechenden Header-Dateien und suchen Sie die Definition.
i486
33
@ i486 - Das ist eine großartige Möglichkeit, spröden, nicht tragbaren Code zu schreiben!
Sean
3
@PanagiotisKanavos C-Header, die Teil der C ++ - Standardbibliothek sind und wahrscheinlich in keinem Ihrer angeblichen "echten C ++" - Header dupliziert werden. Was genau war dein Punkt?
underscore_d
14
Ich habe immer <cstddef>fürstd::size_t
Boiethios
4
@PanagiotisKanavos Sicher, im Allgemeinen ist das ein guter Rat, aber in diesem Fall scheint er nicht relevant zu sein - da es keinen C ++ - Ersatz für std::size_tgibt und das OP nicht befürwortete, ältere C-Funktionen zu verwenden, sondern nur das Zitat zu beachten, dass sie das typedef teilen. Ich bezweifle, dass jemand, der diesen Thread liest, aus diesem Grund in die Irre geführt wird, ältere Typen / Funktionen zu verwenden, aber wenn Sie sicher sein möchten, dass dies nicht der Fall ist, dann fair genug!
underscore_d

Antworten:

90

Angenommen, ich wollte die Funktionen und Typen, die ich importierte, minimieren, würde ich gehen, cstddefda es keine Funktionen deklariert und nur 6 Typen deklariert. Die anderen konzentrieren sich auf bestimmte Domänen (Zeichenfolgen, Zeit, E / A), die für Sie möglicherweise nicht von Bedeutung sind.

Beachten Sie, dass cstddefnur die Definition std::size_t, size_tdh die Definition im Namespace std, garantiert wird, obwohl dieser Name möglicherweise auch im globalen Namespace (effektiv, einfach size_t) bereitgestellt wird .

Im Gegensatz dazu stddef.hgarantiert (was auch ein in C verfügbarer Header ist), dass er size_tim globalen Namespace definiert wird, und kann auch bereitstellen std::size_t.

Sean
quelle
3
Gibt es eine Garantie dafür, dass size_tfrom cstddefdas gleiche ist und immer das gleiche sein wird wie die anderen? Es scheint, dass es eine gemeinsame Header-Datei mit gemeinsamen Definitionen wie size_t...
SnakeDoc
1
@SnakeDoc und wie durch Zauberei hat eine andere Antwort hier bereits genau das beobachtet, über einen 'internen' Header.
underscore_d
5
@SnakeDoc Ja, und dieser Header ist cstddef.
user253751
2
@SnakeDoc, wer sagt, dass sie ihre eigenen definieren? Der Standard sagt nur, dass es nach dem Einfügen dieser Header definiert wird. Es heißt nicht, dass alle es neu definieren müssen. Sie können alle enthalten <cstddef>, oder sie können alle einen internen Header enthalten, der nur definiert size_t.
Jonathan Wakely
1
Ist das csttddefin der Antwort ein Tippfehler? Vielleicht cstddefist gemeint?
Erik Sjölund
46

Tatsächlich definiert die Zusammenfassung (im C ++ - Standard enthalten) mehrerer Header, die spezifisch enthalten sind size_t, sowie weitere Header den Typ size_t(basierend auf dem C-Standard, da die <cX>Header nur ISO C- <X.h>Header mit angegebenen Änderungen sind, bei denen das Entfernen size_tnicht angegeben ist).

Der C ++ Standard jedoch bezieht sich auf <cstddef>die Definition vonstd::size_t

  • in 18.2 Typen ,
  • in 5.3.3 Sizeof ,
  • in 3.7.4.2 Freigabefunktionen ( siehe 18.2) und
  • in 3.7.4.1 Zuordnungsfunktionen ( siehe auch 18.2).

Daher und aufgrund der Tatsache, dass <cstddef>nur Typen und keine Funktionen eingeführt werden, würde ich mich an diesen Header halten, um ihn std::size_tverfügbar zu machen .


Beachten Sie einige Dinge:

  1. Die Art von std::size_tist decltypeohne Verwendung eines Headers erhältlich

    Wenn Sie planen , eine typedef in Ihrem Code einzuführen sowieso (zB weil Sie schreiben einen Behälter und wollen die Bereitstellung eines size_typetypedef) können Sie den globalen verwenden können sizeof, sizeof...oder alignofBetreiber Ihre Art zu definieren , ohne Header überhaupt einschließlich da theose Operatoren geben std::size_tpro Standard Definition und Sie können decltypeauf ihnen verwenden:

    using size_type = decltype(alignof(char));
  2. std::size_tist per se nicht global sichtbar, obwohl Funktionen mit std::size_tArgumenten sind.

    Die implizit deklarierten globalen Zuordnungs- und Freigabefunktionen

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

    NICHT einführen size_t, stdoder std::size_tund

    Verweis auf stdoder std::size_tist schlecht geformt, es sei denn, der Name wurde durch Einfügen des entsprechenden Headers deklariert.

  3. Der Benutzer kann möglicherweise nicht neu definieren, std::size_tobwohl es möglich ist, mehrere Typedefs zu haben, die auf denselben Typ im selben Namespace verweisen.

    Obwohl das Auftreten mehrerer Definitionen von size_tinnerhalb stdgemäß 7.1.3 / 3 vollkommen gültig ist, dürfen namespace stdgemäß 17.6.4.2.1 / 1 keine Erklärungen hinzugefügt werden :

    Das Verhalten eines C ++ - Programms ist undefiniert, wenn es dem Namespace std oder einem Namespace innerhalb des Namespace std Deklarationen oder Definitionen hinzufügt, sofern nicht anders angegeben.

    Das Hinzufügen eines richtigen typedef für size_tzum Namespace verstößt nicht gegen 7.1.3 , aber gegen 17.6.4.2.1 und führt zu undefiniertem Verhalten.

    Erläuterung: Versuchen Sie, 7.1.3 nicht falsch zu interpretieren, und fügen Sie keine Deklarationen oder Definitionen hinzu std(mit Ausnahme einiger Fälle von Vorlagenspezialisierung, in denen ein typedef keine Vorlagenspezialisierung ist). Erweiterung dernamespace std

Pixelchemist
quelle
1
Sie vermissen die Tatsache, dass ein doppeltes typedef keinen neuen Typ einführt. Es wird lediglich ein doppeltes typedef hinzugefügt, das vollkommen gültig ist.
Maxim Egorushkin
@ MaximEgorushkin: Ich behaupte nicht, dass das Hinzufügen eines neu definierten Typedef stdungültig ist, da doppelte Typedefs illegal sind. Ich erkläre, dass es illegal ist, weil Sie einfach keine Definitionen hinzufügen dürfen namespace std- egal ob sie legal wären.
Pixelchemist
Was könnte angesichts all dessen, was wir aus all diesen Standardzitaten wissen, möglicherweise kaputt gehen?
Maxim Egorushkin
12
@ MaximEgorushkin: Alles. Darum geht es bei undefiniertem Verhalten, nicht wahr? Der Punkt , dass es kann funktionieren der Punkt oder sogar , dass es sich nicht auf jeder beliebigen Compiler bricht nicht das Verhalten des Programms machen , definiert gemäß der Norm. Oder wie 'fredoverflow' es hier schön ausdrückt : "Der C ++ - Standard hat die einzige Stimme, Punkt."
Pixelchemist
Ich möchte, dass Sie Ihr kritisches Denken anwenden. Was könnte möglicherweise brechen?
Maxim Egorushkin
9

Alle Standard-Bibliotheksheaderdateien haben dieselbe Definition. Es spielt keine Rolle, welche Sie in Ihren eigenen Code aufnehmen. Auf meinem Computer habe ich die folgende Erklärung in _stddef.h. Diese Datei ist in jeder von Ihnen aufgelisteten Datei enthalten.

/*
   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
quelle
2
Ich bin mir nicht sicher, aber ich denke, es ist wichtig für die Kompilierungszeit, nein?
idclev 463035818
@ tobi303 nicht für diese spezielle Frage. Ja, Sie können einen größeren Header als erforderlich hinzufügen, aber dann haben Sie bereits einen C-Header in einem C ++ - Projekt hinzugefügt. Warum brauchst du überhaupt size_t?
Panagiotis Kanavos
Es ist keine gute Idee, OS-Makro-Sniffing zum Definieren zu verwenden size_t. Sie können es portabler definieren als using size_t = decltype( sizeof( 42 ) ). Aber es gibt keine Notwendigkeit, da <stddef.h>fast keine Kosten entstehen.
Prost und hth. - Alf
4

Sie könnten auf einen Header verzichten:

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

Dies liegt daran, dass der C ++ - Standard Folgendes erfordert:

Das Ergebnis von sizeofund sizeof...ist eine Konstante vom Typ std::size_t. [Hinweis: std::size_tist im Standardheader <cstddef>(18.2) definiert. - Endnote]

Mit anderen Worten, der Standard verlangt:

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

Beachten Sie auch, dass es vollkommen in Ordnung ist, diese typedefDeklaration im globalen und im stdNamespace abzugeben , solange sie mit allen anderen typedefDeklarationen desselben Typedef-Namens übereinstimmt (bei nicht übereinstimmenden Deklarationen wird ein Compilerfehler ausgegeben).

Das ist weil:

  • §7.1.3.1 Ein typedef-Name führt keinen neuen Typ ein, wie es eine Klassendeklaration (9.1) oder eine Enum-Deklaration tut.

  • §7.1.3.3 In einem bestimmten Nichtklassenbereich typedefkann ein Bezeichner verwendet werden, um den Namen eines in diesem Bereich deklarierten Typs neu zu definieren, um auf den Typ zu verweisen, auf den er sich bereits bezieht.


Für Skeptiker, die sagen, dass dies eine Hinzufügung eines neuen Typs zum Namespace darstellt stdund eine solche Handlung durch den Standard ausdrücklich verboten ist, und dies ist UB und das ist alles da; Ich muss sagen, dass diese Haltung bedeutet, ein tieferes Verständnis der zugrunde liegenden Probleme zu ignorieren und zu leugnen.

Der Standard verbietet das Hinzufügen neuer Deklarationen und Definitionen zum Namespace, stdda der Benutzer auf diese Weise die Standardbibliothek durcheinander bringen und sein gesamtes Bein abschießen kann. Für die Standardautoren war es einfacher, den Benutzer auf einige bestimmte Dinge spezialisieren zu lassen und alles andere aus gutem Grund zu verbieten, als alles zu verbieten, was der Benutzer nicht tun sollte, und das Risiko einzugehen, etwas Wichtiges (und dieses Bein) zu verpassen. Sie haben dies in der Vergangenheit getan, als sie forderten, dass kein Standardcontainer mit einem unvollständigen Typ instanziiert werden soll, während dies tatsächlich einige Container tun könnten (siehe Der Standardbibliothekar: Container unvollständiger Typen von Matthew H. Austern ):

... Am Ende schien alles zu trüb und zu schlecht verstanden; Das Standardisierungskomitee glaubte nicht, dass es eine andere Wahl gab, als zu sagen, dass STL-Container nicht mit unvollständigen Typen arbeiten sollten. Aus gutem Grund haben wir dieses Verbot auch auf den Rest der Standardbibliothek angewendet.

... Rückblickend scheint diese Entscheidung, nachdem die Technologie besser verstanden wurde, im Grunde immer noch richtig zu sein. Ja, in einigen Fällen ist es möglich, einige der Standardcontainer so zu implementieren, dass sie mit unvollständigen Typen instanziiert werden können - aber es ist auch klar, dass dies in anderen Fällen schwierig oder unmöglich wäre. Es war größtenteils Zufall, dass der erste Test, den wir versuchten, std::vectoreiner der einfachen Fälle war.

Angesichts der Tatsache, dass die Sprachregeln std::size_tgenau sein müssen decltype(sizeof(int)), ist das Tun namespace std { using size_t = decltype(sizeof(int)); }eines der Dinge, die nichts kaputt machen.

Vor C ++ 11 gab es keine decltypeund daher keine Möglichkeit, die Art des sizeofErgebnisses in einer einfachen Anweisung zu deklarieren, ohne dass viele Vorlagen beteiligt waren. size_tAliase für verschiedene Typen auf verschiedenen Zielarchitekturen. Es wäre jedoch keine elegante Lösung, einen neuen integrierten Typ nur für das Ergebnis von hinzuzufügen sizeof, und es gibt keine standardmäßigen integrierten Typedefs. Daher bestand die portabelste Lösung zu dieser Zeit darin, size_tTypalias in einen bestimmten Header einzufügen und diesen zu dokumentieren.

In C ++ 11 gibt es jetzt eine Möglichkeit, die genaue Anforderung des Standards als eine einfache Deklaration aufzuschreiben.

Maxim Egorushkin
quelle
6
@ Sean Was du geschrieben hast, macht keinen Sinn.
Maxim Egorushkin
15
@ MaximEgorushkin Die Hälfte von ihnen hat diesen Code nicht verstanden ... er funktioniert perfekt. Ich mag diesen Weg jedoch nicht: Es ist imo besser, einen Header einzuschließen und ihn vom Standard definieren zu lassen.
Boiethios
9
Leute, lerne wenigstens die effing Sprache, bevor du perfekt richtige Antworten abstimmst.
Frédéric Hamidi
11
Tom sagte: "Es gibt 6 Standard-Bibliotheks-Header, die dasselbe definieren! Das ist verrückt! Wir brauchen eine und nur eine Definition von size_t!" Eine Minute später sagte Mary: "OMG! Es gibt 7 Definitionen für size_tStandard-Bibliotheksheader und einen Projektheader, den Tom bearbeitet! Es gibt wahrscheinlich mehr in den Bibliotheken von Drittanbietern!" xkcd.com/927
6
size_tDies ist zwar eine mögliche Definition von , beantwortet aber nicht die eigentliche Frage des OP: Es ist, als hätte ich nach dem Header gefragt, in dem FILEdeklariert ist, und Sie würden vorschlagen, meinen eigenen zu schreiben.
Edmz