Allocator Aware Container和propagate_on_container_swap

Sil*_*ler 3 c++ c++11

std::allocator_traits模板定义了一些常量,比如propagate_on_container_copy/move_assign让其他容器知道在复制或移动操作期间是否应该复制第二个容器的分配器.

我们还有propagate_on_container_swap,它指定是否应在交换操作期间复制分配器.

是不是真的有必要为一个分配器意识到容器检查allocator_traits<A>::propagate_on_container_swapContainer::swap()?通常,我按如下方式实现swap:

Container::swap(Container& other)
{
   Container tmp(std::move(other));
   other = std::move(*this);
   *this = std::move(tmp);
}
Run Code Online (Sandbox Code Playgroud)

换句话说,我只是根据移动分配实现交换.由于移动分配操作已经必须处理分配器感知(通过检查propagate_on_container_move_assign),是否可以Container::swap()像这样实现,而不是编写一个完全不同的swap函数,显式检查propagate_on_container_swap

How*_*ant 6

重要的是要区分标准对代码的要求与它对std :: lib的实现者提供的类型的要求之间的区别.

容器需求指定std :: containers必须如何表现.但是,您可以随意编写容器.除非你将容器输入到需要std :: container行为的std :: code中,否则你很高兴.只有几个这样的地方.例如,如果你适应你Containerstd::stack,那么你将不得不提供标准的行为,如果你期望std::stack根据标准的行为.

再回到你的问题,如果你希望你Container有相同的行为在这方面的std ::容器之一,那么你将不得不通过检查,并遵守所有的的propagate_on特质,和所有的其他分配的要求.这是一项非常重要的任务.而且我不一定推荐它.

std :: containers将不执行容器移动构造,也不会移动分配swap.他们将交换他们的内部表示.他们将根据propagate_on_container_swap是否交换分配器来决定(在编译时).

如果propagate_on_container_swap是,它们将交换分配器和内部表示.同样在这种情况下,swapnoexcept在C++ 1z(我们希望是C++ 17)和转发.

如果propagate_on_container_swap为false,则不应交换分配器,甚至不需要交换分配器Swappable.但是容器内部仍然是交换的.在这种情况下,如果两个分配器不比较相等,则行为未定义.

如果您保持Container::swap原样,并std::stack根据您创建Container,std::stack::swap则不会有标准行为.但是,它会有你的行为Container::swap,如果对于所说的客户端std::stack和他们可能使用的任何分配器都没问题,那么就不会有任何伤害. std::stack::swap不会因为你没有严格遵循std :: containers所需的分配器的所有复杂细节而以神秘的方式行事.

  • 那么如果 `propagate_on_container_swap` 为 false,`swap` 就不会是 `noexcept` 吗?这是为什么?如果在这种情况下你不打算对分配器做任何事情,那么为什么它会影响“noexcept”呢? (2认同)
  • 当前的规范草案说:`noexcept(propagate_on_container_swap || is_always_equal)`其中,is_always_equal是一个新特性,如果分配器总是比较相等,则为true。如果这两个都是错误的,那么如果分配器不相等,那么您将进入UB。那时,承诺不扔是没有意义的。什么都可能发生。如果分配器*相等*,则“交换”将不会抛出。但是在这种情况下,直到运行时您都不知道它们是相等的,因此使用`noexcept`做出编译时承诺是没有意义的。 (2认同)
  • 谢谢您的澄清,我正在查看n4140,它没有列出各种容器的任何`noexcept`规范,而且一般容器要求也没有说明`is_always_equal`特性(因为它在该草案:))。n4296与您所说的完全匹配。 (2认同)