¿Qué encabezado debo incluir para `size_t`?

95

Según cppreference.com size_t se define en varios encabezados, a saber

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

Y, desde C ++ 11, también en

<cstdlib>
<cwchar> 

En primer lugar, me pregunto por qué es así. ¿No está esto en contradicción con el principio DRY ? Sin embargo, mi pregunta es:

¿Cuál de los encabezados anteriores debo incluir para usar size_t? ¿Importa en absoluto?

idclev 463035818
fuente
1
Abra los archivos de encabezado correspondientes y busque la definición.
i486
33
@ i486 - ¡Esa es una excelente manera de escribir código frágil no portátil!
Sean
3
@PanagiotisKanavos Encabezados de C que forman parte de la biblioteca estándar de C ++ y probablemente no estén duplicados en ninguno de sus supuestos encabezados de 'verdadero C ++'. ¿Cuál fue tu punto, exactamente?
underscore_d
14
Siempre usé <cstddef>parastd::size_t
Boiethios
4
@PanagiotisKanavos Claro, generalmente es un buen consejo, pero en este caso no parece relevante, ya que no hay un reemplazo de C ++ std::size_ty el OP no abogaba por el uso de funciones C heredadas, solo observando la cita sobre ellos compartiendo el typedef. Dudo que alguien que lea este hilo sea engañado al usar tipos / funciones heredados debido a esto, pero si quieres estar seguro de que no lo hacen, ¡entonces es justo!
underscore_d

Respuestas:

90

Suponiendo que quisiera minimizar las funciones y los tipos que estaba importando, iría con, cstddefya que no declara ninguna función y solo declara 6 tipos. Los demás se centran en dominios particulares (cadenas, tiempo, IO) que pueden no importarle.

Tenga en cuenta que cstddefsolo garantiza definir std::size_t, es decir, definir size_ten el espacio de nombres std, aunque puede proporcionar este nombre también en el espacio de nombres global (efectivamente, sin formato size_t).

Por el contrario, stddef.h(que también es un encabezado disponible en C) garantiza definir size_ten el espacio de nombres global, y también puede proporcionar std::size_t.

Sean
fuente
3
¿Existe alguna garantía de que size_tde cstddefes igual y siempre será igual que los demás? Parece que debería haber un archivo de encabezado común con definiciones comunes como size_t...
SnakeDoc
1
@SnakeDoc y como por arte de magia, otra respuesta aquí ya ha observado exactamente que eso sucede, a través de un encabezado 'interno'.
underscore_d
5
@SnakeDoc Sí, y ese encabezado es cstddef.
user253751
2
@SnakeDoc, ¿quién dice que ellos definen el suyo? Todo lo que dice el estándar es que se definirá después de incluir esos encabezados, no dice que todos tengan que redefinirlo. Todos podrían incluir <cstddef>, o todos podrían incluir algún encabezado interno que solo defina size_t.
Jonathan Wakely
1
¿La csttddefrespuesta es un error tipográfico? ¿Quizás cstddefse refiere?
Erik Sjölund
46

De hecho, la sinopsis (incluida en el estándar C ++) de varios encabezados se incluye específicamente size_t, así como otros encabezados definen el tipo size_t(según el estándar C, ya que los <cX>encabezados son solo <X.h>encabezados ISO C con cambios notados donde size_tno se indica la eliminación de ).

Sin embargo, el estándar C ++ se refiere a <cstddef>la definición destd::size_t

  • en 18.2 Tipos ,
  • en 5.3.3 Tamaño de ,
  • en 3.7.4.2 Funciones de desasignación (que se refiere a 18.2) y
  • en 3.7.4.1 Funciones de asignación (también se refiere a 18.2).

Por lo tanto, y debido al hecho de que <cstddef>solo introduce tipos y no funciones, me quedaría con este encabezado para que esté std::size_tdisponible.


Tenga en cuenta algunas cosas:

  1. El tipo de std::size_tse puede obtener decltypesin incluir un encabezado

    Si planea introducir un typedef en su código de todos modos (es decir, porque escribe un contenedor y desea proporcionar un size_typetypedef), puede usar los operadores global sizeof, sizeof...or alignofpara definir su tipo sin incluir ningún encabezado, ya que esos operadores regresan std::size_tpor definición estándar y puede usar decltypeen ellos:

    using size_type = decltype(alignof(char));
  2. std::size_tno es per se globalmente visible, aunque las funciones con std::size_targumentos sí lo son.

    Las funciones de asignación y desasignación globales declaradas implícitamente

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

    NO introduzca size_t, stdo std::size_ty

    en referencia a std, o std::size_testá mal formada, a menos que el nombre ha sido declarado mediante la inclusión en el encabezado correspondiente.

  3. El usuario no puede redefinir, std::size_taunque es posible tener múltiples typedefs que se refieran al mismo tipo en el mismo espacio de nombres.

    Aunque, la ocurrencia de múltiples definiciones de size_tinside stdes perfectamente válida según 7.1.3 / 3 , no se permite agregar ninguna declaración namespace stdsegún 17.6.4.2.1 / 1 :

    El comportamiento de un programa C ++ no está definido si agrega declaraciones o definiciones al espacio de nombres std oa un espacio de nombres dentro del espacio de nombres std a menos que se especifique lo contrario.

    Agregar un typedef apropiado para size_tel espacio de nombres no viola 7.1.3 pero viola 17.6.4.2.1 y conduce a un comportamiento indefinido.

    Aclaración: Trate de no malinterpretar 7.1.3 y no agregue declaraciones o definiciones std(excepto en algunos casos de especialización de plantilla donde un typedef no es una especialización de plantilla). Extendiendo elnamespace std

Pixelchemist
fuente
1
Se pierde el hecho de que un typedef duplicado no introduce un nuevo tipo. Simplemente agrega un typedef duplicado, que es perfectamente válido.
Maxim Egorushkin
@MaximEgorushkin: No afirmo que agregar una redefinición de typedef a stdno sea válido porque las typedefs duplicadas son ilegales. Declaro que es ilegal porque simplemente no puede agregar definiciones namespace std, sin importar si serían legales.
Pixelchemist
¿Qué podría romperse potencialmente, dado todo lo que sabemos de todas estas citas estándar?
Maxim Egorushkin
12
@MaximEgorushkin: Cualquier cosa. De eso se trata el comportamiento indefinido, ¿no? El hecho de que pueda funcionar o incluso el hecho de que no se rompa en ningún compilador arbitrario no hace que el comportamiento del programa se defina de acuerdo con el estándar. O como 'fredoverflow' lo expresó muy bien aquí : "El estándar C ++ tiene el único voto, punto".
Pixelchemist
Me gustaría que utilizara su pensamiento crítico. ¿Qué podría romperse potencialmente?
Maxim Egorushkin
9

Todos los archivos de encabezado de biblioteca estándar tienen la misma definición; no importa cuál incluya en su propio código. En mi computadora, tengo la siguiente declaración en formato _stddef.h. Este archivo está incluido en todos los archivos que enumeró.

/*
   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
fuente
2
no estoy seguro, pero creo que sí importa para el tiempo de compilación, ¿no?
idclev 463035818
@ tobi303 no para esta pregunta específica. Sí, puede agregar un encabezado más grande de lo necesario, pero luego ya agregó un encabezado C en un proyecto C ++. ¿Por qué lo necesitas size_ten primer lugar?
Panagiotis Kanavos
No es una buena idea utilizar el rastreo de macros del sistema operativo para definir size_t. Puede definirlo de manera más portátil como using size_t = decltype( sizeof( 42 ) ). Pero no es necesario, ya que <stddef.h>tiene un costo casi nulo.
Saludos y hth. - Alf
4

Podrías prescindir de un encabezado:

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

Esto se debe a que el estándar C ++ requiere:

El resultado de sizeofy sizeof...es una constante de tipo std::size_t. [Nota: std::size_tse define en el encabezado estándar <cstddef>(18.2). - nota final]

En otras palabras, el estándar requiere:

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

También tenga en cuenta que está perfectamente bien hacer esta typedefdeclaración en el stdespacio global y en el espacio de nombres, siempre que coincida con todas las demás typedefdeclaraciones del mismo typedef-name (se emite un error del compilador en declaraciones que no coinciden).

Esto es porque:

  • §7.1.3.1 Un typedef-name no introduce un nuevo tipo como lo hace una declaración de clase (9.1) o una declaración de enumeración.

  • §7.1.3.3 En un ámbito que no sea de clase dado, typedefse puede usar un especificador para redefinir el nombre de cualquier tipo declarado en ese ámbito para hacer referencia al tipo al que ya se refiere.


A los escépticos que dicen que esto constituye una adición de un nuevo tipo en el espacio de nombres std, y tal acto está explícitamente prohibido por el estándar, y esto es UB y eso es todo; Debo decir que esta actitud equivale a ignorar y negar una comprensión más profunda de los problemas subyacentes.

El estándar prohíbe agregar nuevas declaraciones y definiciones en el espacio de nombres stdporque al hacerlo el usuario puede hacer un lío de la biblioteca estándar y dispararse. Para los escritores estándar, era más fácil dejar que el usuario se especializara en algunas cosas específicas y prohibir hacer cualquier otra cosa por si acaso, en lugar de prohibir todo lo que el usuario no debería hacer y arriesgarse a perderse algo importante (y esa pierna). Lo hicieron en el pasado cuando requerían que ningún contenedor estándar se instanciara con un tipo incompleto, mientras que, de hecho, algunos contenedores podrían funcionar bien (ver The Standard Librarian: Containers of Incomplete Types por Matthew H. Austern ):

... Al final, todo parecía demasiado turbio y mal entendido; el comité de estandarización no pensó que había otra opción excepto decir que los contenedores STL no deben funcionar con tipos incompletos. Por si acaso, también aplicamos esa prohibición al resto de la biblioteca estándar.

... En retrospectiva, ahora que se comprende mejor la tecnología, esa decisión todavía parece básicamente correcta. Sí, en algunos casos es posible implementar algunos de los contenedores estándar para que se puedan instanciar con tipos incompletos, pero también está claro que en otros casos sería difícil o imposible. Fue principalmente casualidad que la primera prueba que probamos, usando std::vector, resultó ser uno de los casos fáciles.

Dado que las reglas del lenguaje requieren std::size_tser exactas decltype(sizeof(int)), hacer namespace std { using size_t = decltype(sizeof(int)); }es una de esas cosas que no rompen nada.

Antes de C ++ 11 no decltypehabía forma de declarar el tipo de sizeofresultado en una declaración simple y, por lo tanto, no existía una buena cantidad de plantillas. size_talias diferentes tipos en diferentes arquitecturas de destino, sin embargo, no sería una solución elegante agregar un nuevo tipo integrado solo por el resultado de sizeof, y no hay typedefs estándar integrados. Por lo tanto, la solución más portátil en ese momento era poner un size_talias de tipo en algún encabezado específico y documentarlo.

En C ++ 11 ahora hay una forma de escribir ese requisito exacto del estándar como una simple declaración.

Maxim Egorushkin
fuente
6
@Sean Lo que escribiste no tiene ningún sentido.
Maxim Egorushkin
15
@MaximEgorushkin La mitad de ellos no entendió este código ... funciona perfectamente. Sin embargo, no me gusta esta forma: es mejor, en mi opinión, incluir un encabezado y dejar que el estándar lo defina.
Boiethios
9
Chicos, al menos aprendan el maldito lenguaje antes de ir a votar las respuestas perfectamente correctas.
Frédéric Hamidi
11
Tom dijo: "¡Hay 6 encabezados de biblioteca estándar que definen lo mismo! ¡Eso es una locura! ¡Necesitamos una y solo una definición de size_t!" Un minuto más tarde, Mary dijo: "¡Dios mío! ¡Hay 7 definiciones de size_tencabezados de biblioteca estándar y un encabezado de proyecto que Tom está editando! ¡Probablemente hay más en las bibliotecas de terceros!" xkcd.com/927
6
Si bien esa es una posible definición de size_t, esto no responde a la pregunta real del OP: es como si le pidiera el encabezado donde FILEse declara y usted sugeriría escribir uno propio.
edmz