propagate_on_container_move_assignment的示例用法

Sil*_*ler 18 c++ allocator c++11

我试图了解如何正确编写AllocatorAware容器.

我的理解是propagate_on_container_move_assignmenttypedef表示Allocator当Container本身被移动分配时是否需要复制某种类型.

所以,既然我找不到任何这方面的例子,我自己的抨击就像下面这样:

给定容器类型Container,Allocator类型allocator_type和内部allocator_type数据成员m_alloc:

Container& operator = (Container&& other)
{
  if (std::allocator_traits<allocator_type>::propagate_on_container_move_assignment::value)
  {
     m_alloc = std::allocator_traits<allocator_type>::select_on_container_copy_construction(
      other.m_alloc
     );
  }

  return *this;
}
Run Code Online (Sandbox Code Playgroud)

它是否正确?

此外,另一个混乱的来源是嵌套的typedef propagate_on_container_move/copy_assignment专门讨论赋值 ...但是构造函数呢?移动构造函数或AllocatorAware容器的复制构造函数是否需要检查这些typedef?我认为这里的答案是肯定的 ......意思是,我还需要写:

Container(Container&& other)
{
      if (std::allocator_traits<allocator_type>::propagate_on_container_move_assignment::value)
      {
         m_alloc = std::allocator_traits<allocator_type>::select_on_container_copy_construction(
          other.m_alloc
         );
      }
}
Run Code Online (Sandbox Code Playgroud)

How*_*ant 25

我建议研究libc ++<vector>标题.您将不得不处理std :: lib实现者需要使用的所有令人讨厌的下划线.但是libc ++有一个符合C++ 11的实现,用于检查.

移动赋值运算符

容器移动赋值运算符必须处理三种不同的可能性:

  1. propagate_on_container_move_assignment 是真的.
  2. propagate_on_container_move_assignment 是假的,来自lhs和rhs的分配器相等.
  3. propagate_on_container_move_assignment 是错误的,来自lhs和rhs的分配器比较不等.

如果可能,这三种情况之间的决定应该在编译时进行,而不是在运行时进行.具体来说,应该在编译时在集合{1}和{2,3}之间进行选择,因为propagate_on_container_move_assignment它是编译时常量.编译时常量的编译时分支通常使用标记调度来完成,而不是在显示时使用if语句.

在这些情况下都不应该select_on_container_copy_construction使用.该函数适用于容器复制构造函数.

在情况1中,lhs应首先使用lhs的分配器来释放已分配的所有内存.这必须首先完成,因为rhs分配器可能无法在以后释放此内存.然后lhs分配器从rhs分配器移动分配(就像任何其他移动分配一样).然后将内存所有权从rhs容器传输到lhs容器.如果容器的设计使得rhs容器不能处于无资源状态(设计不良),则可以通过rhs分配器的移动来为rhs容器分配新资源.

如果propagate_on_container_move_assignment为false,则必须在运行时在情况2和3之间进行选择,因为分配器比较是运行时操作.

在案例2中,您可以执行与案例1 相同的操作,不要移动分配分配器.跳过这一步.

在案例3中,您无法将任何内存的所有权从rhs容器转移到lhs容器.你唯一能做的就是:

assign(make_move_iterator(rhs.begin()), make_move_iterator(rhs.end()));
Run Code Online (Sandbox Code Playgroud)

请注意,在情况1中,因为算法是在编译时选择的value_type,所以容器的内容不必是MoveAssignablenor MoveInsertable(MoveConstructible)来移动 - 分配容器.但在情况2中,value_type小号必须MoveAssignableMoveInsertable(MoveConstructible),即使他们从来都是,因为你在2和3之间在运行时进行选择.并且3需要这些操作value_type来做assign.

移动赋值运算符很容易成为容器实现的最复杂的特殊成员.其余的更容易:

移动构造函数

移动构造函数只是移动构造分配器并从rhs窃取资源.

复制构造函数

复制构造函数从中获取其分配器select_on_container_copy_construction(rhs.m_alloc),然后使用它为复制分配资源.

复制赋值运算符

复制赋值运算符必须首先检查是否propagate_on_container_copy_assignment为真.如果是,并且如果lhs和rhs分配器比较不相等,那么lhs必须首先释放所有内存,因为在分配器被分配后,它将无法再这样做.接下来,如果propagate_on_container_copy_assignment,复制分配分配器,否则不.然后复制元素:

assign(rhs.begin(), rhs.end());
Run Code Online (Sandbox Code Playgroud)

  • 从 C++17 开始,`if constexpr` 可用于分隔 {1} 和 {2,3}。 (2认同)