Czy w C ++ 20 mogą istnieć różne niejawne obiekty na podstawie późniejszej decyzji w czasie wykonywania?

11

To pytanie dotyczy dodania P0593 do najnowszej wersji C ++ 20 .

Oto mój przykład:

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

Czy ten kod jest dobrze zdefiniowany dla wszystkich danych wejściowych w najnowszej wersji roboczej?

Uzasadnienie wyrażone w P0593 wyjaśnia dość wyraźnie, że odkomentowanie [2]prowadziłoby do nieokreślonego zachowania z powodu ścisłego naruszenia aliasingu, jeśli dwa elementy wprowadzone przez użytkownika różnią się. Domniemane stworzenie obiektu powinno nastąpić tylko raz, w punkcie malloc; nie jest wywoływane przez instrukcję przypisania w foo.

Dla każdego faktycznego uruchomienia programu istnieje element nieokreślonego zestawu niejawnych obiektów, który sprawiłby, że program byłby dobrze zdefiniowany. Ale nie jest dla mnie jasne, czy wybór tworzenia niejawnego obiektu, o którym mowa w [intro.object] / 10, musi być dokonany, kiedy to mallocnastąpi; lub czy decyzja może dotyczyć „podróży w czasie”.

Ten sam problem może pojawić się w przypadku programu, który odczytuje binarny obiekt blob do bufora, a następnie podejmuje w czasie wykonywania decyzję, w jaki sposób uzyskać do niego dostęp (np. Deserializacja; a nagłówek mówi nam, czy nadchodzi liczba zmiennoprzecinkowa czy int).

MM
źródło

Odpowiedzi:

9

Domniemane stworzenie obiektu powinno nastąpić tylko raz, w punkcie malloc; nie jest wywoływane przez instrukcję przypisania w foo.

To nie ma znaczenia. Liczy się to, który obiekt zostanie utworzony. Standard mówi, że tworzony obiekt to taki, który tworzy coś, co byłoby UB w dobrze zdefiniowanym kodzie:

operacja ta niejawnie tworzy i uruchamia czas życia zerowego lub więcej obiektów typu niejawnego życia ([podstawowe.typy]) w określonym regionie pamięci, jeśli spowodowałoby to zdefiniowanie przez program zachowania.

Zachowanie jest ostatecznie oparte na wykonaniu środowiska wykonawczego, a nie na analizie statycznej. Musisz więc tylko śledzić wykonanie programu, dopóki nie natrafisz na przypadek, w którym zachowanie nie zostanie zdefiniowane, ale zostanie zdefiniowane, jeśli jakiś obiekt zostanie utworzony w tym magazynie w czasie danej operacji.

Lokalizacja tworzenia jest więc zawsze „operacją”, ale określenie, co zostanie utworzone, zależy od tego, jak pamięć zostanie wykorzystana w czasie wykonywania (tj. Zachowanie).

Nicol Bolas
źródło
2
Żeby było jasne, mówisz, że mój kod jest dobrze zdefiniowany?
MM
2
@MM: Zgadza się.
Nicol Bolas,