Могут ли быть разные неявные объекты, основанные на более позднем решении времени выполнения в C ++ 20?

11

Этот вопрос относится к добавлению P0593 к последней версии C ++ 20 .

Вот мой пример:

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

Правильно ли определен этот код для всех входных данных по последнему проекту?

Обоснование, выраженное в P0593, ясно показывает, что раскомментирование [2]может привести к неопределенному поведению из-за строгого нарушения псевдонимов, если два элемента пользовательского ввода различаются. Предполагается, что создание неявного объекта происходит только один раз, в точке malloc; это не вызвано оператором присваивания в foo.

Для любого фактического запуска программы существует член неопределенного набора неявных объектов, который сделал бы программу хорошо определенной. Но мне не ясно, нужно ли делать выбор неявного создания объекта, упомянутого в [intro.object] / 10, когда это mallocпроисходит; или может ли решение "путешествие во времени".

Та же проблема может возникнуть для программы, которая считывает двоичный двоичный объект в буфер, а затем принимает решение во время выполнения о том, как получить к нему доступ (например, десериализация; заголовок сообщает нам, будет ли всплывать float или int).

М.М.
источник

Ответы:

9

Предполагается, что создание неявного объекта происходит только один раз, в точке malloc; это не вызвано оператором присваивания в foo.

Это не актуально. Важно то, какой объект создается. Стандарт гласит, что создаваемый объект - это объект, который превращает что-то, что было бы в UB, в четко определенный код:

эта операция неявно создает и запускает время жизни нуля или более объектов типов неявного времени жизни ([basic.types]) в указанной области памяти, если это приведет к тому, что программа будет иметь определенное поведение.

Поведение в конечном счете основано на выполнении во время выполнения, а не на статическом анализе. Таким образом, вам нужно только следить за выполнением программы, пока вы не столкнетесь с ситуацией, когда поведение не будет определено, но будет определено, если бы объект некоторого типа был создан в этом хранилище во время рассматриваемой операции.

Таким образом, местоположение создания всегда является «операцией», но определение того, что создается, основано на том, как память используется во время выполнения (то есть: поведение).

Николь Болас
источник
2
Чтобы быть понятным, вы говорите, что мой код четко определен?
ММ
2
@ ММ: Это правильно.
Никол Болас