c++ c++11 std language-lawyer

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 de sizeof y sizeof... es una constante de tipo std::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 tipo size_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 tipo unsigned long

y

El tipo de sizeof ... es una constante de tipo std::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) {}