【发布时间】:2013-12-26 01:32:11
【问题描述】:
关于 copy-and-swap 习语有几个很好的答案,例如 explaining the copy and swap idiom 和 explaining move semantics。适用于复制和移动分配的基本习惯用法如下所示:
T& T::operator=(T other) {
this->swap(other);
return *this;
}
此赋值适用于复制和移动赋值,因为 other 是复制还是移动构造取决于赋值的右侧是左值还是右值。
现在让有状态的分配器进入画面:如果T 被参数化为分配器类型,例如std::vector<S, A>,上述习语并不总是有效!具体来说,std::allocator_traits<A> 包含三种类型,指示是否应该传播分配器:
std::allocator_traits<A>::propagate_on_container_copy_assignmentstd::allocator_traits<A>::propagate_on_container_move_assignmentstd::allocator_traits<A>::propagate_on_container_swap
这三个特征的默认值是std::false_type(参见 20.6.8.1 [allocator.traits.types] 第 7、8 和 9 段)。如果这些特征中的任何一个是std::false_type 并且分配器是有状态的并且可能比较不相等,则正常的复制和交换习语不起作用。对于复制分配,修复相当简单:
T& T::operator= (T const& other) {
T(other, this->get_allocator()).same_allocator_swap(*this);
return *this;
}
也就是说,首先复制对象并提供 LHS 的分配器对象,然后使用一个函数交换成员,该函数在两个对象使用相同的分配器时起作用,即other.get_allocator() == this->get_allocator()。
移动分配时,如果可以移动,最好不要复制 RHS。如果分配器相同,则可以移动 RHS。否则需要使用适当的分配器复制对象,从而导致像这样的赋值运算符
T& T::operator= (T&& other) {
T(std::move(other), this->get_allocator()).same_allocator_swap(*this);
return *this;
}
这里的方法是移动构造一个临时的同时也传递一个分配器。这样做假定T 类型确实有一个“移动构造函数”,它采用T&& 作为对象状态和一个分配器来指定要使用的分配器。根据分配器的不同或相同,将负担放在移动构造函数上进行复制或移动。
由于第一个参数的传递方式不同,因此不能将复制和移动赋值合并到赋值运算符的一个版本中。因此,他们需要将参数作为引用,并且需要显式复制或移动参数以抑制复制省略的可能性。
当涉及分配器时,是否有更好的方法来处理赋值运算符?
【问题讨论】: