为什么在C++ 11(涉及分配器)中交换标准库容器会有问题?

Fil*_*efp 13 c++ language-lawyer c++11

注:由Originially问GreenScape评论.


阅读之后为什么STL容器中的交换成员函数未声明为noexcept?似乎在为标准容器执行时潜在的未定义行为的原因a.swap(b)归结为也交换或不交换底层分配器.

  • 为什么交换分配器和数据有问题?

Fil*_*efp 13

让我们开始深入研究标准(N3797):

23.2.1p9 一般集装箱要求 [container.requirements.general]

如果 allocator_traits<allocator_type>::propagate_on_container_swap::valuetrue,那么也应该使用对非成员的未经授权的呼叫来交换a和的分配器.否则,它们不应被交换,并且行为是未定义的,除非.bswapa.get_allocator() == b.get_allocator()


目的是propagate_on_container_swap什么?

如果一个Allocator有一个名为的typedefpropagate_on_container_swap引用std::true_type了两个被交换的容器的底层分配器,那么它也会交换.[1]

如果propagate_on_container_swapstd::false_type只有两个容器的数据会掉,但是分配器将继续留在自己的位置.

[1]这意味着之后a.swap(b),a.get_allocator()将是以前的b.get_allocator(); 分配器已交换.


有状态分配器的含义是什么?

Allocator不仅负责为标准容器中的元素分配内存,它们还负责释放所述元素.

C++ 03不允许在标准容器中使用有状态分配器,但是C++ 11必须要求支持这样的分配器.这意味着我们可以定义一个分配器,它取决于它的构造方式,以某种方式运行.

如果分配器propagate_on_container_swap::value等于false所涉及的两个分配器之间的状态差异,则可能导致未定义的行为,因为分配器的一个实例可能与另一个实例处理的数据不兼容.


如果没有正确交换有状态分配器可能会出现什么问题?

假设我们有一个MagicAllocator使用mallocoperator new分配内存,具体取决于它的构造方式.

如果它用于malloc分配内存,则必须使用它free来释放它,如果是operator new,delete则需要; 因此,它必须保留一些信息,说明它应该使用哪两个.

如果我们有两个std::vector都使用MagicAllocator但具有不同的状态(意味着一个使用malloc和另一个operator new),并且我们不在分配器上交换a.swap(b)分配器将不匹配在交换后为两个向量中的元素分配的内存 -这就意味着在解除分配时可能会调用错误free/ delete.

  • @MattMcNabb 我为你写了这个:[为什么允许`propagate_on_container_swap == false` 可能会导致未定义的行为?](http://stackoverflow.com/q/23758399/1090079) (2认同)