Почему это не поведение undefined, при котором std :: uninitialized_copy обычно разыменовывает итератор на неинициализированную память? - PullRequest
3 голосов
/ 27 мая 2020

Я знаю, что разыменование указателя или итератора, указывающего на неинициализированную память, является незаконным, если только это не специальный итератор, такой как std::raw_storage_iterator.

Тогда мне кажется странным, что семейство std::uninitialized_ алгоритмов, кажется, это делают? Например, Эквивалентное поведение для std::uninitialized_copy в соответствии с § 23.10.10.4 стандарта C ++ 17 определяется следующим образом:

template <class InputIterator, class ForwardIterator> ForwardIterator uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result);

Эффекты : Как будто:

for (; first != last; ++d_first, (void) ++first) ::new (static_cast<void*>(addressof(*result))) typename iterator_traits<ForwardIt>::value_type(*first);

Где result - это ForwardIterator для диапазона неинициализированной памяти . Аналогично, пример en.cppreference и G CC 7,5 (строка 83) делают это. Я, должно быть, что-то упускаю; почему это законно? Я специально имею в виду:

static_cast<void*>(addressof(*result))

1 Ответ

3 голосов
/ 27 мая 2020

Я знаю, что разыменование указателя или итератора, указывающего на неинициализированную память, недопустимо.

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

std::addressof не обращается к значению указанного объекта. Требуется только его адрес. Это то, что разрешено для объектов до и после их жизни, пока их хранилище было выделено.

Даже если это не так из-за некоторых технических особенностей правил, реализация стандартной библиотеки не обязательно ограничивается правила языка.


Стандартные цитаты (последний проект):

[basi c .life]

До времени существования объекта был запущен, но после того, как хранилище, которое будет занимать объект, будет выделено ... любой указатель, представляющий адрес хранилища, в котором будет ... расположен объект, может быть использован, но только ограниченными способами. Информацию о строящемся или разрушающемся объекте см. В [class.cdtor]. В противном случае такой указатель относится к выделенной памяти ([basi c .st c .dynami c .allocation]), и использование указателя, как если бы указатель имел тип void *, четко определено. Косвенное обращение через такой указатель разрешено , но результирующее lvalue может использоваться только ограниченными способами, как описано ниже. Программа имеет неопределенное поведение, если: (здесь нет подходящих случаев)

Точно так же до начала жизненного цикла объекта, но после того, как было выделено хранилище, которое объект будет занимать. . любое значение glvalue, которое относится к исходному объекту, может использоваться, но только ограниченным образом. Информацию о строящемся или разрушающемся объекте см. В [class.cdtor]. В противном случае такое glvalue относится к выделенному хранилищу ([basi c .st c .dynami c .allocation]), а с использованием свойств glvalue, которые не зависят от его значения, хорошо - определено . Программа имеет неопределенное поведение, если: (здесь нет подходящих случаев)

...