当涉及分配器时,是否存在类似于复制和交换习惯用法的内容?

Die*_*ühl 9 c++ allocator c++11

关于复制和交换习语有几个很好的答案,例如,解释复制和交换习语以及解释移动语义.适用于复制和移动分配的基本习惯用法如下所示:

T& T::operator=(T other) {
    this->swap(other);
    return *this;
}
Run Code Online (Sandbox Code Playgroud)

此赋值适用于复制和移动赋值,因为other复制或移动构造取决于赋值的右侧是左值还是右值.

现在让我们让有状态的分配器进入图片:如果T在分配器类型上参数化,例如,std::vector<S, A>上面的成语并不总是有效!具体来说,std::allocator_traits<A>包含三种类型,指示是否应传播分配器:

  1. std::allocator_traits<A>::propagate_on_container_copy_assignment
  2. std::allocator_traits<A>::propagate_on_container_move_assignment
  3. std::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;
}
Run Code Online (Sandbox Code Playgroud)

也就是说,首先复制对象,提供LHS的分配器对象,然后使用一个函数交换成员,如果两个对象都使用相同的分配器,即何时使用other.get_allocator() == this->get_allocator().

移动分配时,如果可以移动RHS,则不要复制RHS.如果分配器相同,则可以移动RHS.否则,需要使用适当的分配器复制对象,从而导致这样的赋值运算符

T& T::operator= (T&& other) {
    T(std::move(other), this->get_allocator()).same_allocator_swap(*this);
    return *this;
}
Run Code Online (Sandbox Code Playgroud)

这里的方法是将构造临时移动,同时传递分配器.这样做假定类型T确实有一个"移动构造函数",同时T&&为对象状态和分配器指定要使用的分配器.移动构造函数的负担是根据分配器的不同或相同而复制或移动.

由于第一个参数的传递方式不同,因此复制和移动赋值不能折叠到赋值运算符的一个版本中.因此,他们需要将他们的参数作为参考,并且需要明确地复制或移动抑制复制省略的可能性的论据.

在涉及分配器时,是否有更好的方法来处理赋值运算符?

How*_*ant 5

你似乎暗示着经典的复制/交换成语为所有的情况下工作的propagate_on_特点是不假.我不相信这种情况.例如考虑:

std::allocator_traits<A>::propagate_on_container_copy_assignment::value == true
std::allocator_traits<A>::propagate_on_container_swap::value == false
Run Code Online (Sandbox Code Playgroud)

经典的复制/交换习语分配运算符不会将分配器从rhs传播到lhs,而是在两个分配器状态不相等的情况下进入未定义行为的状态.

您对复制赋值运算符的重写也不适用于此propagate_on_特征组合,因为它从不在复制赋值上传播分配器.

如果有人想遵循std :: containers的规则,我不相信复制/交换习惯用法.

我为自己保留了一个"分配器行为"备忘单,描述了这些成员应该如何表现(英语而不是标准eze).

复制赋值运算符

如果propagate_on_container_copy_assignment::value为true,则copy指定分配器.在这种情况下,如果在赋值之前分配器不相等,则首先释放lhs上的所有内存.然后继续复制分配值,而不转移任何内存所有权.即this->assign(rhs.begin(), rhs.end()).

移动赋值运算符

  1. 如果propagate_on_container_move_assignment::value为true,则取消分配lhs上的所有内存,移动分配分配器,然后将内存所有权从rhs转移到lhs.

  2. 如果propagate_on_container_move_assignment::value为false,并且两个分配器相等,则释放lhs上的所有内存,然后将内存所有权从rhs转移到lhs.

  3. 如果propagate_on_container_move_assignment::value为false,并且两个分配器不相等,则移动assign,就好像this->assign(make_move_iterator(rhs.begin()), make_move_iterator(rhs.end()).

这些描述旨在实现最高性能,同时遵守容器和分配器的C++ 11规则.只要有可能,内存资源(例如vector capacity())就会从rhs传输,或者在lhs上重用.

复制/交换习惯用法总是丢弃vector capacity()lhs上的内存资源(例如),而是在lhs上解除分配它们之前暂时分配这些资源.

为了完整性:

交换

如果propagate_on_container_swap::value是,则交换分配器.无论如何,交换内存所有权.如果propagate_on_container_swap::value为false且分配器不相等,则行为未定义.