tipos sintaxis programas programar funciones ejemplos datos como comandos codigos c++ c++11 language-lawyer static-typing dynamic-typing

sintaxis - ¿Hasta qué punto es C++ un lenguaje de tipo estático?



sintaxis de c++ (3)

Solía ​​pensar que la respuesta a esta pregunta era " 100% ", pero recientemente se me ha señalado un ejemplo que hace que valga la pena pensarlo dos veces. Considere una matriz C declarada como un objeto con duración de almacenamiento automático:

int main() { int foo[42] = { 0 }; }

Aquí, el tipo de foo es claramente int[42] . Considere, en cambio, este caso:

int main() { int* foo = new int[rand() % 42]; delete[] foo; }

Aquí, el tipo de foo es int* , pero ¿cómo se puede saber el tipo de objeto creado por la new expresión en tiempo de compilación? (El énfasis está destinado a subrayar el hecho de que no estoy hablando del puntero devuelto por la new expresión, sino del objeto de matriz creado por la new expresión).

Esto es lo que el Párrafo 5.3.4 / 1 del Estándar de C ++ 11 especifica sobre el resultado de una new expresión:

[...] Las entidades creadas por una nueva expresión tienen una duración de almacenamiento dinámico (3.7.4). [Nota: la vida útil de una entidad de este tipo no se limita necesariamente al ámbito en el que se crea. —Endente final] Si la entidad no es un objeto de matriz, la nueva expresión devuelve un puntero al objeto creado. Si es una matriz, la nueva expresión devuelve un puntero al elemento inicial de la matriz.

Solía ​​pensar que en C ++ el tipo de todos los objetos se determina en tiempo de compilación, pero el ejemplo anterior parece refutar esa creencia. Asimismo, según el párrafo 1.8 / 1:

[...] Las propiedades de un objeto se determinan cuando se crea el objeto . Un objeto puede tener un nombre (Cláusula 3). Un objeto tiene una duración de almacenamiento (3.7) que influye en su vida útil (3.8). Un objeto tiene un tipo (3.9). [...]

Así que mis preguntas son:

  1. ¿Qué se entiende por " propiedades " en el último párrafo citado? Claramente, el nombre de un objeto no puede contar como algo que se determina " cuando se crea el objeto ", a menos que " creado " aquí signifique algo diferente de lo que creo;
  2. ¿Hay otros ejemplos de objetos cuyo tipo se determina solo en tiempo de ejecución?
  3. ¿Hasta qué punto es correcto decir que C ++ es un lenguaje estático? O más bien, ¿cuál es la forma más adecuada de clasificar C ++ a este respecto?

Sería genial si alguien pudiera elaborar al menos en uno de los puntos anteriores.

EDITAR:

El estándar parece dejar en claro que la new expresión sí crea un objeto de matriz , y no solo varios objetos dispuestos como una matriz como lo señalan algunos. Según el párrafo 5.3.4 / 5 (cortesía de Xeo ):

Cuando el objeto asignado es una matriz (es decir, se utiliza la sintaxis noptr-new-declarator o la nueva id-type o type-id denota un tipo de matriz), la nueva-expresión produce un puntero al elemento inicial (si cualquiera) de la matriz. [Nota: tanto la new int como la new int[10] tienen el tipo int* y el tipo de la new int[i][10] es int (*)[10] —tendéncia] El atributo-especificador-seq en un noptr- El nuevo declarador pertenece al tipo de matriz asociado .


Solía ​​pensar que en C ++ el tipo de todos los objetos se determina en tiempo de compilación, pero el ejemplo anterior parece refutar esa creencia.

El ejemplo que cita se refiere a la duración de almacenamiento del elemento. C ++ reconoce tres duraciones de almacenamiento:

  1. La duración del almacenamiento estático es la duración de las variables estáticas globales y locales.
  2. La duración del almacenamiento automático es la duración de las variables locales de función de "pila asignada".
  3. La duración del almacenamiento dinámico es la duración de la memoria asignada dinámicamente, como la de new or malloc .

El uso de la palabra "dinámico" aquí no tiene nada que ver con el tipo de objeto. Se refiere a cómo una implementación debe almacenar los datos que conforman un objeto.

Solía ​​pensar que en C ++ el tipo de todos los objetos se determina en tiempo de compilación, pero el ejemplo anterior parece refutar esa creencia.

En su ejemplo, hay una variable, que tiene el tipo int* . No hay un tipo de matriz real para la matriz subyacente que se puede recuperar de cualquier manera significativa para el programa. No hay mecanografía dinámica.


La nueva expresión no crea un objeto con un tipo de matriz que varía en tiempo de ejecución. Crea muchos objetos, cada uno de tipo estático int . El número de estos objetos no se conoce estáticamente.

C ++ proporciona dos casos (sección 5.2.8) para el tipo dinámico:

  • Igual que el tipo estático de la expresión.
  • Cuando el tipo estático es polimórfico, el tipo de tiempo de ejecución del objeto más derivado

Ninguno de estos proporciona a ningún objeto creado por new int[N] un tipo de matriz dinámica.

Pedanticalmente, la evaluación de la nueva expresión crea un número infinito de objetos de matriz superpuestos. Desde 3.8p2:

[Nota: La vida útil de un objeto de matriz se inicia tan pronto como se obtiene el almacenamiento con el tamaño y la alineación adecuados, y su vida útil finaliza cuando el almacenamiento que ocupa la matriz se reutiliza o se libera. 12.6.2 describe la vida útil de los subobjetos base y miembro. - nota final]

Entonces, si desea hablar sobre el "objeto de matriz" creado por el new int[5] , debe asignarle no solo el tipo int[5] sino también el int[4] , int[1] , char[5*sizeof(int)] , y struct s { int x; }[5] struct s { int x; }[5] .

Sostengo que esto es equivalente a decir que los tipos de matriz no existen en el tiempo de ejecución. El tipo de un objeto se supone que es restrictivo, información y le informa algo sobre sus propiedades. Permitir que un área de memoria se trate como un número infinito de objetos de matriz superpuestos con un tipo diferente en efecto significa que el objeto de matriz es completamente sin tipo de letra. La noción de tipo de tiempo de ejecución solo tiene sentido para los objetos de elementos almacenados dentro de la matriz.


Los términos ''tipo estático'' y ''tipo dinámico'' se aplican a las expresiones.

tipo estático

tipo de expresión (3.9) resultante del análisis del programa sin considerar la semántica de ejecución


tipo dinámico

Tipo <glvalor> del objeto más derivado (1.8) al que se refiere el glvalor denotado por una expresión de glvalue

Además, puede ver que un tipo dinámico solo difiere de un tipo estático cuando el tipo estático se puede derivar, lo que significa que un tipo de matriz dinámica siempre es el mismo que el tipo estático de la expresión.

Así que tu pregunta:

pero, ¿cómo se puede saber el tipo de objeto creado por la nueva expresión en tiempo de compilación?

Los objetos tienen tipos, pero no son tipos "estáticos" o "dinámicos" en ausencia de una expresión que se refiera al objeto. Dada una expresión, el tipo estático siempre se conoce en tiempo de compilación. En ausencia de derivación, el tipo dinámico es el mismo que el tipo estático.

Pero estás preguntando sobre los tipos de objetos independientes de las expresiones. En el ejemplo que da, ha pedido que se cree un objeto, pero no especifica el tipo de objeto que desea crear en el momento de la compilación. Puedes verlo así:

template<typename T> T *create_array(size_t s) { switch(s) { case 1: return &(*new std::array<T, 1>)[0]; case 2: return &(*new std::array<T, 2>)[0]; // ... } }

Hay poco especial o único sobre esto. Otra posibilidad es:

struct B { virtual ~B() {}}; struct D : B {}; struct E : B {}; B *create() { if (std::bernoulli_distribution(0.5)(std::default_random_engine())) { return new D; } return new E; }

O:

void *create() { if (std::bernoulli_distribution(0.5)(std::default_random_engine())) { return reinterpret_cast<void*>(new int); } return reinterpret_cast<void*>(new float); }

La única diferencia con el new int[] es que no puede ver su implementación para ver cómo se selecciona entre diferentes tipos de objetos para crear.