c++ - ¿`Sizeof`*realmente*evalúa a` std:: size_t`? ¿Puede?
c++11 language-lawyer (5)
El estándar simplemente exige que el tipo de sizeof(expr)
sea del mismo tipo que std::size_t
. No hay ningún mandato de que el uso de sizeof(expr)
haga que el nombre std::size_t
esté disponible y, dado que std::size_t
simplemente nombra uno de los tipos integrales integrados, en realidad no hay ningún problema.
Tome el siguiente pasaje estándar:
[C++11: 5.3.3/6]:
el resultado desizeof
ysizeof...
es una constante de tipostd::size_t
. [Nota:std::size_t
se define en el encabezado estándar<cstddef>
(18.2). -finalizar nota]
Ahora:
[C++11: 18.2/6]:
el tiposize_t
es un tipo de entero sin signo definido por la implementación que es lo suficientemente grande como para contener el tamaño en bytes de cualquier objeto.
De acuerdo, el pasaje no requiere que size_t
sea un alias de tipo definido con typedef
, pero dado que está explícitamente establecido como disponible por el encabezado estándar <cstddef>
, creo que podemos leer como leído que no incluir <cstddef>
debería eliminar cualquier garantía de que size_t
estará disponible para un programa.
Sin embargo, según esa primera cita, podemos obtener una expresión de tipo std::size_t
independientemente.
De hecho, podemos demostrar ambos hechos :
int main()
{
typedef decltype(sizeof(0)) my_size_t;
my_size_t x = 0; // OK
std::size_t y = 1; // error: ''size_t'' is not a member of ''std''
}
std::size_t
no es visible para el programa, pero sizeof(0)
todavía nos da uno? De Verdad?
Por lo tanto, no es correcto decir que 5.3.3/6
es defectuoso , y que en realidad tiene "el mismo tipo que lo que std::size_t
resuelve", pero no std::size_t
sí mismo?
Claro, los dos son uno y lo mismo si std::size_t
es un alias de tipo pero, de nuevo, en realidad no se requiere esto.
Es del mismo tipo, pero debe incluir ese encabezado para usarlo.
No confundas el mapa con el territorio.
Los tipos pueden nombrarse por nombres tipo. Estos nombres de tipos pueden estar incorporados, pueden ser tipos definidos por el usuario o incluso pueden ser parámetros de template
y hacer referencia a múltiples tipos diferentes según la creación de instancias.
Pero los nombres no son los tipos. Claramente, el estándar no exige que todos los tipos tengan nombres, la struct {}
clásica struct {}
es un tipo sin nombre.
std::size_t
es un nombre de tipo. Nombra el tipo que devuelve sizeof(expression)
.
El compilador podría tener un nombre canónico para el tipo - __size_t
sería una forma de tener un nombre de tipo canónico único incorporado.
El estándar garantiza en esa cláusula que cualquiera sea el tipo de sizeof(expression)
, una vez que #include <cstddef>
, el nombre std::size_t
ahora se refiere a ese tipo.
En el estándar, se refieren a tipos por nombres. No dicen "el tipo al que se refiere este nombre de tipo", sino que simplemente dicen "el tipo $ NAME $". El compilador podría decidir que int
es otro nombre para __int_32_fast
si quisiera, y el estándar tampoco tendría objeciones.
Esto mismo ocurre con std::nullptr_t
y std::initializer_list<Ts>
y std::type_info
: el uso de variables de esos tipos no siempre requiere que el encabezado que le proporciona un nombre para esos tipos se incluya en su programa .
Los tipos tradicionales incorporados en C / C ++ tenían nombres canónicos que no requerían un encabezado. La desventaja es que esto rompe el código existente, ya que los nuevos nombres de tipos en el ámbito global colisionan con otros identificadores.
Al tener "tipos sin nombre", donde puede obtener un nombre para ellos a través de un archivo de encabezado, evitamos ese problema.
Sí.
El tipo generado por sizeof
es un tipo entero sin signo; la implementación define cuál es.
Por ejemplo, en una implementación particular, el tipo de un sizeof
expresión puede ser unsigned long
.
std::size_t
, si es typedef
, no es más que un nombre alternativo para unsigned long
. Entonces estas dos declaraciones:
El tipo de
sizeof ...
es una constante de tipounsigned long
y
El tipo de
sizeof ...
es una constante de tipostd::size_t
están diciendo exactamente lo mismo para esa implementación . El tipo unsigned long
y el tipo std::size_t
son del mismo tipo. La diferencia es que este último es exacto para todas las implementaciones (conformes), donde std::size_t
podría ser un alias para, digamos, unsigned int
o algún otro tipo sin firmar.
En lo que respecta al compilador, sizeof
produce un resultado de tipo unsigned long
; el compilador (a diferencia de la biblioteca de tiempo de ejecución) no necesita tener ningún conocimiento del nombre size_t
.
Todo esto asume que std::size_t
(o simplemente size_t
si estás hablando de C) es un typedef. Eso no está explicado en el estándar C o C ++. Sin embargo, una implementación puede ajustarse directamente a los requisitos del estándar haciendo que size_t
typedef. No creo que haya otra forma portátil de satisfacer esos requisitos. (No puede ser una macro o una palabra clave definida por la implementación porque eso infringiría en el espacio de nombre del usuario y una macro no se delimitaría en el std
nombres std
). Un compilador podría hacer que size_t
una construcción específica de la implementación distinta de un typedef, pero como un typedef funciona perfectamente, no tiene sentido hacerlo. Sería bueno, en mi humilde opinión, si el estándar estableciera que size_t
es un typedef.
(Un lado irrelevante: el problema real es que el estándar se refiere al resultado como una "constante" .En ISO C, una "constante" es un token, como un entero literal. C ++, que yo sepa, doesn '' t define el sustantivo "constante", pero se refiere a la definición ISO del término. sizeof ...
es una expresión constante; no es una constante . Llamar al resultado un "valor constante" hubiera sido razonable.)
Tal como lo entiendo, este pasaje estándar requiere la siguiente expresión:
typeid(sizeof(0)) == typeid(std::size_t)
siempre cederá true
. Si usa el identificador real std::size_t
, ::size_t
o cualquier otro alias / typedef será irrelevante siempre que se conserve la identidad del tipo, como por std::typeinfo::operator==()
.
El mismo problema de identidad de tipo aparece en otros lugares del idioma. Por ejemplo, en mi máquina de 64 bits, el siguiente código no se compila debido a la redefinición de la función:
#include <cstddef>
void foo(std::size_t x)
{}
void foo(unsigned long x)
{}