¿Por qué no podemos crear objetos trivialmente construibles usando malloc si el constructor trivial predeterminado no realiza ninguna acción?

14

Tengo dificultades para comprender el siguiente párrafo citado de cppreference sobre el constructor trivial predeterminado. He buscado stackoverflow pero aún no obtuve una respuesta clara. Así que por favor ayuda.

Un constructor predeterminado trivial es un constructor que no realiza ninguna acción. Todos los tipos de datos compatibles con el lenguaje C (tipos POD) son trivialmente construibles por defecto. Sin embargo, a diferencia de C, los objetos con constructores triviales predeterminados no se pueden crear simplemente reinterpretando el almacenamiento adecuadamente alineado, como la memoria asignada con std :: malloc: position-new es necesaria para introducir formalmente un nuevo objeto y evitar un posible comportamiento indefinido.

Específicamente, si el constructor trivial predeterminado no hace nada, ¿por qué no podemos reinterpretar el almacenamiento y pretender que hay un objeto con el tipo dado? ¿Podría proporcionar algunos ejemplos del posible comportamiento indefinido que esto causaría?

Liu Sha
fuente
El trabajo más importante de un compilador no es compilar el código fuente, sino rechazar el código posiblemente no válido. No puede hacer esto cuando usa malloc ().
Hans Passant el
66
El razonamiento es muy simple. Cuantas menos oportunidades tenga el programador para hacer locuras, más oportunidades tendrá el compilador para hacer locuras (optimizaciones agresivas).
n. 'pronombres' m.
1
Por razones similares que no puedes simplemente *reinterpret_cast<float*>(&someNonFloatObject) = 0.1f;. C ++ tiene un concepto de objetos y vidas de objeto, especificado en la máquina abstracta, y el hecho de que no haya instrucciones de CPU para crear un objeto desde el almacenamiento no significa que no haya diferencia en la máquina abstracta.
Max Langhof
1
@HansPassant Un compilador que rechaza todo el código rechaza todo el código no válido. De todos modos, no es trabajo del copulador rechazar programas que tienen UB.
n. 'pronombres' m.
1
stackoverflow.com/q/52154744
StoryTeller - Unslander Monica

Respuestas:

7

P0593R5 da este ejemplo:

struct X { int a, b; };
X *make_x() {
  X *p = (X*)malloc(sizeof(struct X));
  p->a = 1;
  p->b = 2;
  return p;
}

y explica:

Cuando se compila con un compilador de C ++, este código tiene un comportamiento indefinido, porque p-> a intenta escribir en un subobjeto int de un objeto X, y este programa nunca creó un objeto X ni un subobjeto int.

Por [intro.object] p1,

Un objeto se crea mediante una definición, mediante una nueva expresión, cuando se cambia implícitamente el miembro activo de una unión, o cuando se crea un objeto temporal.

... y este programa no hizo ninguna de estas cosas.

En la práctica, esto funciona y la situación de UB se considera más como un defecto en el estándar que cualquier otra cosa. Todo el objetivo del documento es proponer una forma de solucionar ese problema y casos similares sin romper otras cosas.

Un programador
fuente
1

Por razones de "pureza".

La alternativa y el status quo real era que cada región de almacenamiento contendría todos los objetos que se ajustan en ese almacenamiento, al mismo tiempo. Algunos miembros del comité están incómodos con el statu quo y mucha gente temía la idea de tener infinitos objetos en el mismo lugar (en un estado virtual no inicializado).

Nadie ha podido mostrar un problema lógico al tener infinitos objetos en una región de almacenamiento.

Como tenían diferentes secciones de la norma que decían cosas contradictorias, los miembros del comité decidieron tomarse en serio una de las peores partes de la norma.

Además, el uso de literales de cadena no está estrictamente permitido, si realmente se toma en serio esa parte del estándar.

curioso
fuente
el uso de literales de cadena no está estrictamente permitido . Hay un problema similar de CWG sobre los type_infoobjetos. ¿Has informado sobre los literales de cadena?
Abogado de idiomas