Peut-il y avoir différents objets implicites basés sur une décision d'exécution ultérieure en C ++ 20?

11

Cette question se réfère à l'ajout de P0593 au dernier projet C ++ 20 .

Voici mon exemple:

#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]
}

Ce code est-il bien défini pour toutes les entrées du dernier projet?

La justification exprimée dans P0593 montre assez clairement que la non-mise en commentaire [2]entraînerait un comportement indéfini en raison d'une violation stricte de l'alias, si les deux éléments d'entrée d'utilisateur diffèrent. La création d'objet implicite est censée se produire une seule fois, au point de malloc; il n'est pas déclenché par l'instruction d'affectation dans foo.

Pour toute exécution réelle du programme, il existe un membre de l'ensemble non spécifié d'objets implicites qui rendrait le programme bien défini. Mais il n'est pas clair pour moi si le choix de création d'objet implicite mentionné dans [intro.object] / 10 doit être fait lorsque malloccela se produit; ou si la décision peut "voyager dans le temps".

Le même problème peut survenir pour un programme qui lit un blob binaire dans un tampon puis prend une décision d'exécution sur la façon d'y accéder (par exemple, la désérialisation; et l'en-tête nous indique si un flottant ou un int est à venir).

MM
la source

Réponses:

9

La création d'objet implicite est censée se produire une seule fois, au point de malloc; il n'est pas déclenché par l'instruction d'affectation dans foo.

Ce n'est pas pertinent. Ce qui importe, c'est quel objet est créé. La norme dit que l'objet qui est créé est celui qui fait quelque chose qui aurait été UB dans un code bien défini:

cette opération crée et démarre implicitement la durée de vie de zéro ou plusieurs objets de types à durée de vie implicite ([basic.types]) dans sa région de stockage spécifiée, si cela se traduisait par un comportement défini du programme.

Le comportement est finalement basé sur l'exécution du runtime et non sur une analyse statique. Il vous suffit donc de suivre l'exécution du programme jusqu'à ce que vous rencontriez un cas où le comportement ne serait pas défini, mais serait défini si un objet d'un certain type avait été créé dans ce stockage au moment de l'opération en question.

Ainsi, l'emplacement de création est toujours "l'opération", mais la détermination de ce qui est créé est basée sur la façon dont la mémoire est utilisée au moment de l'exécution (ie: comportement).

Nicol Bolas
la source
2
Pour être clair, vous dites que mon code est bien défini?
MM
2
@MM: C'est exact.
Nicol Bolas