tl; dr:是的,移动std::vector<T, A>
可能会使迭代器无效
常见的情况(std::allocator
到位)是不会发生无效,但不能保证,如果您依靠自己的实现当前不会使迭代器无效的事实,则切换编译器或什至下一次编译器更新可能会使您的代码行为不正确。
在移动分配:
问题是否 std::vector
在移动分配之后,迭代器实际上可以保持有效与向量模板的分配器意识有关,并且取决于分配器类型(可能还有其各自的实例)。
在每一个实现我所看到的,一个的移入分配std::vector<T, std::allocator<T>>
1实际上不会无效迭代器或指针。但是存在一个问题,当归结为使用该标准时,因为该标准不能保证迭代器对于a的任何移动分配仍然有效。std::vector
,因为容器通常是分配器感知的,所以该实例的。
自定义分配器可能具有状态,并且如果它们不随移动分配而传播并且比较不相等,则向量必须使用其自己的分配器为移动的元素分配存储。
让:
std::vector<T, A> a{};
std::vector<T, A> b;
b = std::move(a);
现在如果
std::allocator_traits<A>::propagate_on_container_move_assignment::value == false &&
std::allocator_traits<A>::is_always_equal::value == false &&
(可能从c ++ 17开始)
a.get_allocator() != b.get_allocator()
然后b
将分配新的存储并将元素a
一一移动到该存储中,从而使所有迭代器,指针和引用无效。
原因是满足以上条件1.禁止在容器移动时分配分配器的移动。因此,我们必须处理分配器的两个不同实例。如果这两个分配器对象现在既不总是比较等于(2),也不实际等于相等,则两个分配器的状态都不同。分配器x
可能无法释放y
状态不同的另一个分配器的内存,因此具有分配器的容器x
不能只是从通过分配了其内存的容器中窃取内存y
。
如果分配器在移动分配上传播,或者两个分配器的比较均等,则实现很可能会选择仅生成b
自己a
的数据,因为它可以确保能够正确地释放存储。
1:std::allocator_traits<std::allocator<T>>::propagate_on_container_move_assignment
和std::allocator_traits<std::allocator<T>>::is_always_equal
都是typdef的std::true_type
(对于任何非专业std::allocator
)。
在施工中:
std::vector<T, A> a{};
std::vector<T, A> b(std::move(a));
知道分配器的容器的move构造函数将从当前表达式要从其移动的容器的分配器实例中移动构造其分配器实例。因此,可以确保适当的释放能力,并且可以(实际上将)窃取内存,因为移动构造是(除了std::array
)势必具有恒定的复杂性。
注意:即使对于move构造,仍然不能保证迭代器保持有效。
交换时:
要求两个向量的迭代器在交换后保持有效(现在仅指向相应的交换容器)很容易,因为交换仅在以下情况下具有已定义的行为:
std::allocator_traits<A>::propagate_on_container_swap::value == true ||
a.get_allocator() == b.get_allocator()
因此,如果分配器在交换时不传播并且比较不相等,则交换容器首先是未定义的行为。
a_iter
现在在移动b
之后引用一个元素a
。