¿Puede haber diferentes objetos implícitos basados ​​en una decisión de tiempo de ejecución posterior en C ++ 20?

11

Esta pregunta se refiere a la adición de P0593 al último borrador de C ++ 20 .

Aquí está mi ejemplo:

#include <cstdlib>
#include <cstdio>

void foo(void *p)
{
    if ( std::getchar() == 'i' )
    {
        *(int *)p = 2;
        std::printf("%d\n", *(int *)p);
    }
    else
    {
        *(float *)p = 2;
        std::printf("%f\n", *(float *)p);
    }
}

int main()
{
    void *a = std::malloc( sizeof(int) + sizeof(float) );
    if ( !a ) return EXIT_FAILURE;

    foo(a);
    // foo(a);    [2]
}

¿Está este código bien definido para todas las entradas del último borrador?

La justificación expresada en P0593 deja bastante claro que el descomentar [2]conduciría a un comportamiento indefinido debido a una violación estricta de alias, si los dos elementos de entrada del usuario difieren. Se supone que la creación del objeto implícito ocurre solo una vez, en el punto de malloc; no se activa por la instrucción de asignación en foo.

Para cualquier ejecución real del programa, existe un miembro del conjunto no especificado de objetos implícitos que haría que el programa esté bien definido. Pero no me queda claro si la elección de la creación implícita de objetos mencionada en [intro.object] / 10 debe hacerse cuando mallocsucede; o si la decisión puede "viajar en el tiempo".

El mismo problema puede surgir para un programa que lee un blob binario en un búfer y luego toma una decisión de tiempo de ejecución sobre cómo acceder a él (por ejemplo, deserialización; y el encabezado nos dice si está surgiendo un flotante o un int).

MM
fuente

Respuestas:

9

Se supone que la creación del objeto implícito ocurre solo una vez, en el punto de malloc; no se activa por la instrucción de asignación en foo.

Eso no es relevante. Lo que importa es qué objeto se crea. El estándar dice que el objeto que se crea es uno que hace algo que habría sido UB en un código bien definido:

esa operación crea e inicia implícitamente el tiempo de vida de cero o más objetos de tipos de tiempo de vida implícito ([basic.types]) en su región de almacenamiento especificada si hacerlo daría como resultado que el programa tenga un comportamiento definido.

El comportamiento se basa en última instancia en la ejecución del tiempo de ejecución, no en el análisis estático. Por lo tanto, solo necesita seguir la ejecución del programa hasta que se encuentre con un caso en el que el comportamiento no se definiría, pero se definiría si se hubiera creado un objeto de algún tipo en ese almacenamiento en el momento de la operación en cuestión.

Por lo tanto, la ubicación de la creación siempre es "la operación", pero la determinación de lo que se crea se basa en cómo se usa la memoria en tiempo de ejecución (es decir, el comportamiento).

Nicol Bolas
fuente
2
Para ser claros, ¿estás diciendo que mi código está bien definido?
MM
2
@MM: Eso es correcto.
Nicol Bolas