Kann es verschiedene implizite Objekte geben, die auf einer späteren Laufzeitentscheidung in C ++ 20 basieren?

11

Diese Frage bezieht sich auf die Hinzufügung von P0593 zum neuesten C ++ 20-Entwurf .

Hier ist mein Beispiel:

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

Ist dieser Code für alle Eingaben im neuesten Entwurf genau definiert?

Die in P0593 zum Ausdruck gebrachte Begründung macht ziemlich deutlich, dass das Auskommentieren [2]aufgrund einer strengen Aliasing-Verletzung zu undefiniertem Verhalten führen würde, wenn sich die beiden Benutzereingabeelemente unterscheiden. Die implizite Objekterstellung soll nur einmal an dem Punkt erfolgen malloc; Es wird nicht durch die Zuweisungsanweisung in ausgelöst foo.

Für jede tatsächliche Ausführung des Programms gibt es ein Mitglied der nicht angegebenen Menge impliziter Objekte, die das Programm klar definieren würden. Mir ist jedoch nicht klar, ob die in [intro.object] / 10 erwähnte Wahl der impliziten Objekterstellung getroffen werden muss, wenn mallocdies geschieht. oder ob die Entscheidung "Zeitreise" kann.

Das gleiche Problem kann bei einem Programm auftreten, das einen binären Blob in einen Puffer liest und dann zur Laufzeit entscheidet, wie darauf zugegriffen werden soll (z. B. Deserialisierung; und der Header gibt an, ob ein Float oder ein Int ansteht).

MM
quelle

Antworten:

9

Die implizite Objekterstellung soll nur einmal an dem Punkt erfolgen malloc; Es wird nicht durch die Zuweisungsanweisung in ausgelöst foo.

Das ist nicht relevant. Entscheidend ist, welches Objekt erstellt wird. Der Standard besagt, dass das Objekt, das erstellt wird, eines ist, das etwas, das UB gewesen wäre, zu genau definiertem Code macht:

Diese Operation erstellt und startet implizit die Lebensdauer von null oder mehr Objekten impliziter Lebensdauer ([basic.types]) in ihrem angegebenen Speicherbereich, wenn dies dazu führen würde, dass das Programm ein definiertes Verhalten aufweist.

Das Verhalten basiert letztendlich auf der Laufzeitausführung und nicht auf einer statischen Analyse. Sie müssen also nur die Ausführung des Programms verfolgen, bis Sie auf einen Fall stoßen, in dem das Verhalten nicht definiert wäre, aber definiert wäre, wenn zum Zeitpunkt der betreffenden Operation ein Objekt eines Typs in diesem Speicher erstellt worden wäre.

Der Erstellungsort ist also immer "die Operation", aber die Bestimmung, was erstellt wird, basiert darauf, wie der Speicher zur Laufzeit verwendet wird (dh: Verhalten).

Nicol Bolas
quelle
2
Um es klar zu sagen, Sie sagen, dass mein Code gut definiert ist?
MM
2
@ MM: Das stimmt.
Nicol Bolas