I have read some old posts on propagate_on_container_move_assignment to understand how it works but still not able to wrap my head around some of the finer points. As per my understanding, move constructors work in a simple manner ( move construct allocator along with any internal state) but move assignment operators need to handle when allocators can propagate.
我已经阅读了一些关于Proportate_on_Container_Move_Assignment的旧帖子,以了解它是如何工作的,但仍然无法理解一些更细微的要点。根据我的理解,移动构造函数的工作方式很简单(移动构造分配器和任何内部状态),但移动赋值操作符需要处理分配器何时可以传播。
This answer in the following question two links provide some background on how the allocator propagation work and why it is required
以下问题两个链接中的答案提供了有关分配器传播如何工作以及为什么需要它的一些背景知识
Copied from the above links -
从上述链接复制-
The container move assignment operator must deal with three separate
possibilities:
- propagate_on_container_move_assignment is true.
- propagate_on_container_move_assignment is false, and the allocators from the lhs and rhs compare equal.
- propagate_on_container_move_assignment is false, and the allocators from the lhs and rhs compare unequal.
I understand the first two, however, for the third case "propagate_on_container_move_assignment is false, and the allocators from the lhs and rhs compare unequal." I do not understand how is this not an issue for move constructor. In case 3 for move assignments, we end up copying data as we can not own the memory of rhs allocator. While during move construction, we assume we can always own the memory and just move construct the allocator along with the internal state. If this were possible in move constructor then couldn't we do similar in move assignment - move assign the allocator and move assign the internal state.
然而,我理解前两种情况,对于第三种情况,“PROPACTATE_ON_CONTAINER_MOVE_ASSIGNMENT为FALSE,并且来自LHS和RHS的分配器比较不相等。”我不明白为什么这不是移动构造函数的问题。在情况3中,对于移动分配,我们最终复制数据,因为我们不能拥有RHS分配器的内存。而在Move构造过程中,我们假设我们可以始终拥有内存,并且只需随内部状态一起Move构造分配器。如果这在Move构造函数中是可能的,那么我们不能在Move赋值中做类似的操作吗--Move赋值分配器,Move赋值内部状态。
To me it looks like if case 3 is true for move assignment then that would mean our move constructors as implemented without the "propagate" checks would be ill-formed.
在我看来,如果情况3对于移动赋值是真的,那么这将意味着我们实现的没有“传播”检查的移动构造函数将是病态的。
Please help me understand the concept. Best provide an example where for move assignment we go for 3rd case due to allocator not supporting propagation( there should be a valid reason for the allocator to not support pocma = std::true_type ) but move constructor works?
请帮我理解一下这个概念。最好提供一个例子,对于移动分配,由于分配器不支持传播(应该有一个有效的理由让分配器不支持Pocma=std::TRUE_TYPE),但移动构造函数起作用,我们在哪里进行移动分配?
Thanks,
谢谢,
更多回答
The constructor always installs an allocator (because it starts empty), so there is no option for source and target to be different.
构造函数总是安装一个分配器(因为它开始时是空的),所以没有源和目标不同的选项。
I understand that. But if an allocator is not safe to move (as in # 3 case) then how come constructor does not care about it.
我明白这一点。但是,如果一个分配器移动起来不安全(就像第三种情况一样),那么为什么构造函数不关心它。
Can you give an example where we will go for case 3 in move assignment due to allocator not supporting propagation( there should be a valid reason for the allocator to not support pocma = std::true_type ) but move construction works? I will upadate the question to provide an example.
你能给出一个例子,在移动分配中,由于分配器不支持传播(分配器应该有一个有效的理由不支持Pocma=std::TRUE_TYPE)而移动建筑工程,我们将在移动分配中使用情况3吗?我将更新这个问题以提供一个例子。
I have no examples, but have noted that a move constrcutor for an allocator has the very special requiement that the orignal is unchanged! There is no such requirement for move assignment, but you can instead select not to assign it.
我没有例子,但我注意到分配器的移动构造器有一个非常特殊的要求,那就是原始的不变!对于移动分配没有这样的要求,但您可以选择不分配它。
优秀答案推荐
When propagate_on_container_move_assignment
is false, it does not imply that the allocator "is not safe to move". It implies that the author of the allocator has determined that a container that uses that allocator should not have its allocator changed by a move assignment. std::pmr::polymorphic_allocator
was explicitly designed this way, so that when a container uses std::pmr::polymorphic_allocator
as its allocator, then the allocator of the object can't be changed during the object's lifetime; the creator of the object has sole and absolute control over how that container allocates memory.
当propagate_on_container_move_assignment为false时,它并不意味着分配器“移动不安全”。这意味着分配器的作者已经确定使用该分配器的容器不应该通过移动赋值来更改其分配器。std::pmr::polymorphic_allocator就是这样设计的,所以当一个容器使用std::pmr::polymorphic_allocator作为它的分配器时,对象的分配器在对象的生存期内不能改变;对象的创建者对容器如何分配内存有唯一和绝对的控制权。
As a result of this design decision, the move assignment operator must account for the case where the LHS keeps its original allocator but that allocator is unequal to the RHS's allocator, meaning that the LHS allocator cannot deallocate the memory allocated by the RHS allocator. This means that, for example, in the case of std::vector
's move assignment operator, the LHS cannot simply "steal" the buffer from the RHS because the result would be the LHS owning memory that the LHS's allocator does not know how to deallocate. Instead the move assignment would degenerate into an element-wise move assignment in this case.
作为该设计决策的结果,移动分配操作符必须考虑LHS保留其原始分配器但该分配器不等于RHS的分配器的情况,这意味着LHS分配器不能释放由RHS分配器分配的内存。这意味着,例如,在std::VECTOR的移动赋值操作符的情况下,LHS不能简单地从RHS“窃取”缓冲区,因为结果将是LHS拥有内存,而LHS的分配器不知道如何解除分配。相反,在本例中,移动分配将退化为基于元素的移动分配。
This issue can't arise during move construction because allocators always propagate on move construction. There is no propagate_on_container_move_construction
customization point that can change this behaviour. So the new object gets its allocator from the old object and, therefore, that allocator always knows how to deallocate the buffer from the old object that the new object is now taking ownership of.
在移动构造期间不会出现此问题,因为分配器总是在移动构造上传播。不存在可以更改此行为的PROPACTIVATE_ON_CONTAINER_MOVE_CONSTRUCTION定制点。因此,新对象从旧对象获得其分配器,因此,该分配器总是知道如何从新对象现在取得所有权的旧对象中释放缓冲区。
This situation also can't arise during copy construction because a copy constructor never adopts memory from the source object so it doesn't need to worry about whether the new object's allocator can deallocate any adopted memory. First, the copy constructor determines the allocator that the new object will use; then, it uses that allocator to allocate memory for all the elements in the new object. Clearly, that same allocator will know how to deallocate that memory later.
在复制构造过程中也不会出现这种情况,因为复制构造函数从不从源对象获取内存,因此它不需要担心新对象的分配器是否可以释放任何采用的内存。首先,复制构造函数确定新对象将使用的分配器;然后,它使用该分配器为新对象中的所有元素分配内存。显然,同一个分配器稍后将知道如何释放该内存。
C++98 does not require allocators to be assignable (and indeed there are allocators that do not provide operator=
). For backward compatibility, the library cannot use the assignment operator unless the allocator says it's possible to do so.
C++98不要求分配器是可赋值的(确实存在不提供操作符=的分配器)。为了向后兼容,库不能使用赋值运算符,除非分配器允许这样做。
更多回答
我是一名优秀的程序员,十分优秀!