我知道取消引用指向未初始化内存的指针或迭代器是非法的,除非它是特殊的迭代器,例如std::raw_storage_iterator
。
然后令我感到奇怪的是,std::uninitialized_
系列算法似乎做到了这一点?例如。根据std::uninitialized_copy
的第23.10.10.4条,对于C++17 standard的等效行为表示为:
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的示例和GCC 7.5(第83行)也可以做到这一点。我肯定错过了什么;为什么这是合法的?我特别指的是:
static_cast<void*>(addressof(*result))
我知道取消引用指向未初始化内存的指针或迭代器是非法的
不完全是。单独的间接访问不是非法的。仅在执行诸如取决于值的操作之类的情况下,行为才是未定义的。
std::addressof
不访问引用对象的值。它仅采用其地址。在分配对象的存储空间之前和之后,允许在对象上使用此功能。
即使由于规则中的某些技术原因而并非如此,标准库的实现也不一定受语言规则的限制。
标准引号(最新草案):
[basic.life]
在对象的生存期开始之前但已分配了该对象将要使用的存储空间之后……可以使用任何表示该对象将位于的存储位置地址的指针,但只能在有限的方式。有关正在构造或销毁的对象,请参见[class.cdtor]。否则,这样的指针将引用已分配的存储([basic.stc.dynamic.allocation]),并且使用该指针时就像该指针的类型为void *是明确定义的。允许通过此类指针进行间接访问,但是生成的左值只能以有限的方式使用,如下所述。如果出现以下情况,该程序将具有未定义的行为:(没有适用的情况)
类似地,在对象的生存期开始之前但在该对象将要占用的存储空间已分配之后……可以使用引用原始对象的任何glvalue,但只能以有限的方式使用。有关正在构造或销毁的对象,请参见[class.cdtor]。否则,这样的glvalue指分配的存储([basic.stc.dynamic.allocation]),并且使用不依赖其值的glvalue的属性是明确定义的。如果出现以下情况,该程序将具有未定义的行为:(没有适用的情况)