为什么 `std::uninitialized_copy` 通常将迭代器取消引用到未初始化的内存不是未定义的行为?

Why is it not undefined behavior that `std::uninitialized_copy` typically dereferences an iterator to uninitialized memory?

我知道取消引用指向未初始化内存的指针或迭代器是非法的,除非它是特殊的迭代器,例如 std::raw_storage_iterator.

我觉得 std::uninitialized_ 系列算法似乎这样做很奇怪? 例如。根据 C++17 standard 的第 23.10.10.4 条,std::uninitialized_copy 等效行为 表述如下:

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

Effects: As if by:

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

其中resultForwardIterator到一段未初始化的内存。 同样,en.cppreference's example and GCC 7.5(第 83 行)执行此操作。我肯定错过了什么;为什么这是合法的?我具体指的是:

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

I'm aware that dereferencing a pointer or iterator that points to uninitialized memory is illegal

不完全是。间接本身并不违法。只有在执行依赖于值的操作时行为才未定义。

std::addressof 不访问引用对象的值。它只需要它的地址。这是对象在其生命周期之前和之后分配存储时允许的事情。

即使由于规则中的某些技术性而并非如此,标准库的实现也不一定受语言规则的限制。


标准报价(最新草案):

[basic.life]

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated ... any pointer that represents the address of the storage location where the object will be ... located may be used but only in limited ways. For an object under construction or destruction, see [class.cdtor]. Otherwise, such a pointer refers to allocated storage ([basic.stc.dynamic.allocation]), and using the pointer as if the pointer were of type void* is well-defined. Indirection through such a pointer is permitted but the resulting lvalue may only be used in limited ways, as described below. The program has undefined behavior if: (no cases that apply here)

Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated ... any glvalue that refers to the original object may be used but only in limited ways. For an object under construction or destruction, see [class.cdtor]. Otherwise, such a glvalue refers to allocated storage ([basic.stc.dynamic.allocation]), and using the properties of the glvalue that do not depend on its value is well-defined. The program has undefined behavior if: (no cases that apply here)