Имеет ли std :: unitialized_copy неопределенное поведение? - PullRequest
2 голосов
/ 29 февраля 2020

Обычная идиома при построении буферов (скажем, кольцевого буфера) для объектов типа класса T состоит в инициализации объекта T * с адресом памяти, полученным из std :: mallo c () или оператора new (), и затем для создания объектов в этом буфере по требованию, используя размещение new, используя арифметику указателя c на указатель T для обхода блока памяти.

Хотя весьма маловероятно, что существует какой-либо компилятор, для которого это не будет работать (это работает с g ++ и clang ++), мне кажется, строго говоря, это может иметь неопределенное поведение. Это связано с тем, что §8.7 / 4 C ++ 17, по-видимому, разрешает арифметику указателей c только для массивов, а блок памяти, возвращаемый mallo c, оператором new или оператором new [], не является массивом, поскольку я Я понимаю, что только новое выражение [] может создать массив в динамической памяти c, который, таким образом, будет полностью инициализирован в точке построения.

Это также заставило меня задуматься о том, что эталонная реализация std :: uninitialized_copy имеет неопределенное поведение для динамически выделяемой неинициализированной памяти, потому что его эталонная реализация в §23.10.10.4 / 1 C ++ 17 использует арифметику указателя c на итераторе назначения, который здесь будет указателем.

Возможно, то же самое относится к std :: uninitialized_copy, если неинициализированная память получается нединамически, скажем, с использованием выровненного массива без знака char или std :: byte, как это разрешено в §4.5 / 3 C ++ 17, потому что arithmeti c в §8.7 / 4 подразумевает, что тип указателя назначения, по которому арифметика c должен выполняться тип элемента массива (unsigned char или std :: byte), а не тип, созданный в нем с использованием размещения new.

Это кажется удивительным. Кто-нибудь может указать на недостаток (если таковой имеется) в этом рассуждении.

1 Ответ

1 голос
/ 29 февраля 2020

Да, арифметика указателя c на указатель, возвращенный из malloc или operator new, имеет неопределенное поведение без размещения нового массива ранее (что само по себе не может быть сделано надежно), и поэтому с использованием std::unintialized_copy на нем, который определен так, как если бы этот арифметический указатель c был выполнен.

Лучшее, что вы можете сделать, - это создать массив std::byte (или unsigned char) в качестве хранилища, непосредственно используя new[], а затем размещение новых отдельных объектов в этом массиве хранения, что сделает эти объекты вложенными в массиве буферов.

Арифметика указателей c в массиве хранения четко определена, поэтому вы можете достичь указателей на позиции отдельного объекта в массиве хранения и с помощью std::launder или с помощью указателя, возвращенного из позиции размещения, вы можете получить указатель на вложенный объект. Даже тогда вы не сможете использовать арифметику указателей c для указателей на вложенные объекты, поскольку они не образуют массив.

См. Статью P0593R5 Неявное создание объектов для низкоуровневых манипулирование объектом для дальнейших примеров этого удивительно неопределенного поведения и предложения, чтобы сделать его определенным в будущем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...