我试图了解如何正确编写AllocatorAware容器。
我的理解是propagate_on_container_move_assignment
typedef指示在移动分配Container本身时是否需要复制某个Allocator
类型。
所以,既然我找不到任何这方面的例子,我自己的抨击就像下面这样:
给定容器类型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;
}
这是对的吗?
此外,另一个混乱的来源是嵌套的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
);
}
}
答案 0 :(得分:20)
我建议您研究libc++的<vector>
标题。您必须处理std :: lib实现者需要使用的所有令人讨厌的下划线。但是libc ++有一个符合C ++ 11的实现,用于检查。
移动作业运算符
容器移动赋值运算符必须处理三种不同的可能性:
propagate_on_container_move_assignment
是真的。propagate_on_container_move_assignment
为false,而lhs和rhs的分配器相等。propagate_on_container_move_assignment
是假的,来自lhs和rhs的分配器比较不等。如果可能,这三种情况之间的决定应该在编译时进行,而不是在运行时进行。具体来说,应该在编译时在集合{1}和{2,3}之间进行选择,因为propagate_on_container_move_assignment
是编译时常量。编译时常量的编译时分支通常使用tag dispatching来完成,而不是在显示时使用if语句。
在这些情况下都不应使用select_on_container_copy_construction
。该函数仅为容器拷贝构造函数的 。
在情况1中,lhs应首先使用lhs的分配器来释放已分配的所有内存。这必须首先完成,因为rhs分配器可能无法在以后释放此内存。然后lhs分配器从rhs分配器移动分配(就像任何其他移动分配一样)。然后将内存所有权从rhs容器传输到lhs容器。如果你的容器的设计是这样的,rhs容器不能处于无资源状态(一个糟糕的设计imho),那么一个新的资源可以由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()));
请注意,在案例1中,由于算法是在编译时选择的,因此容器的value_type
不必是MoveAssignable
也不是MoveInsertable
(MoveConstructible
)移动 - 分配容器。但在案例2中,value_type
s 做必须是MoveAssignable
和MoveInsertable
(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());