本文介绍了移动的向量总是空的吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道通常标准对已移出的值几乎没有要求:

I know that generally the standard places few requirements on the values which have been moved from:

N3485 17.6.5.15 [lib.types.movedfrom]/1:

N3485 17.6.5.15 [lib.types.movedfrom]/1:

C++ 标准库中定义的类型的对象可以从 (12.8) 中移出.移动操作可能显式指定或隐式生成.除非另有说明,否则此类移出的对象应处于有效但未指定的状态.

我找不到关于 vector 的任何内容,明确将其排除在本段之外.但是,我无法想出一个理智的实现来导致向量不为空.

I can't find anything about vector that explicitly excludes it from this paragraph. However, I can't come up with a sane implementation that would result in the vector being not empty.

是否有一些标准语需要我遗漏或类似于 basic_string 视为C++03 中的连续缓冲区?

Is there some standardese that entails this that I'm missing or is this similar to treating basic_string as a contiguous buffer in C++03?

推荐答案

我来晚了,并提供了一个额外的答案,因为我认为目前没有任何其他答案是完全正确的.

I'm coming to this party late, and offering an additional answer because I do not believe any other answer at this time is completely correct.

问题:

移动的向量总是空的吗?

答案:

通常,但不,并非总是如此.

Usually, but no, not always.

血腥细节:

vector 没有像某些类型那样标准定义的移动状态(例如,unique_ptr 在被指定为等于 nullptr 之后移自).然而,vector 的要求是这样的,没有太多的选择.

vector has no standard-defined moved-from state like some types do (e.g. unique_ptr is specified to be equal to nullptr after being moved from). However the requirements for vector are such that there are not too many options.

答案取决于我们是在谈论 vector 的移动构造函数还是移动赋值运算符.在后一种情况下,答案还取决于 vector 的分配器.

The answer depends on whether we're talking about vector's move constructor or move assignment operator. In the latter case, the answer also depends on the vector's allocator.

vector<T, A>::vector(vector&& v)

此操作必须具有恒定的复杂性.这意味着别无选择,只能从 v 中窃取资源来构造 *this,让 v 处于空状态.无论分配器 A 是什么,也不管 T 是什么类型,这都是正确的.

This operation must have constant complexity. That means that there are no options but to steal resources from v to construct *this, leaving v in an empty state. This is true no matter what the allocator A is, nor what the type T is.

因此对于移动构造函数,是的,移动的 vector 将始终为空.这不是直接指定的,而是超出了复杂性要求,并且没有其他方法可以实现.

So for the move constructor, yes, the moved-from vector will always be empty. This is not directly specified, but falls out of the complexity requirement, and the fact that there is no other way to implement it.

vector<T, A>&
vector<T, A>::operator=(vector&& v)

这要复杂得多.主要有3种情况:

This is considerably more complicated. There are 3 major cases:

allocator_traits<A>::propagate_on_container_move_assignment::value == true

(propagate_on_container_move_assignment 计算结果为 true_type)

在这种情况下,移动赋值运算符将销毁*this中的所有元素,使用来自*this的分配器释放容量,移动分配分配器,然后转移所有权从 v*this 的内存缓冲区.除了销毁*this中的元素外,这是一个O(1)复杂度的操作.通常(例如,在大多数但不是所有 std::algorithms 中),移动分配的 lhs 在移动分配之前具有 empty() == true.

In this case the move assignment operator will destruct all elements in *this, deallocate capacity using the allocator from *this, move assign the allocators, and then transfer ownership of the memory buffer from v to *this. Except for the destruction of elements in *this, this is an O(1) complexity operation. And typically (e.g. in most but not all std::algorithms), the lhs of a move assignment has empty() == true prior to the move assignment.

注意:在 C++11 中,std::allocatorpropagate_on_container_move_assignmentfalse_type,但这已更改为 true_type 用于 C++1y(我们希望 y == 4).

Note: In C++11 the propagate_on_container_move_assignment for std::allocator is false_type, but this has been changed to true_type for C++1y (y == 4 we hope).

在第一种情况下,移动的 vector 将始终为空.

In case One, the moved-from vector will always be empty.

allocator_traits<A>::propagate_on_container_move_assignment::value == false
    && get_allocator() == v.get_allocator()

(propagate_on_container_move_assignment 计算结果为 false_type,并且两个分配器比较相等)

(propagate_on_container_move_assignment evaluates to false_type, and the two allocators compare equal)

在这种情况下,移动赋值运算符的行为与情况一类似,但有以下例外:

In this case, the move assignment operator behaves just like case One, with the following exceptions:

  1. 分配器没有移动分配.
  2. 这种情况和情况三之间的决定发生在运行时,情况三需要更多的T,因此情况二也是如此,即使情况二实际上并没有执行那些额外的要求在 T 上.
  1. The allocators are not move assigned.
  2. The decision between this case and case Three happens at run time, and case Three requires more of T, and thus so does case Two, even though case Two doesn't actually execute those extra requirements on T.

如果是二,移动的 vector 将始终为空.

In case Two, the moved-from vector will always be empty.

allocator_traits<A>::propagate_on_container_move_assignment::value == false
    && get_allocator() != v.get_allocator()

(propagate_on_container_move_assignment 计算结果为 false_type,并且两个分配器比较不相等)

(propagate_on_container_move_assignment evaluates to false_type, and the two allocators do not compare equal)

在这种情况下,实现不能移动分配分配器,也不能将任何资源从 v 传输到 *this(资源是内存缓冲区).在这种情况下,实现移动赋值运算符的唯一方法是有效地:

In this case the implementation can not move assign the allocators, nor can it transfer any resources from v to *this (resources being the memory buffer). In this case, the only way to implement the move assignment operator is to effectively:

typedef move_iterator<iterator> Ip;
assign(Ip(v.begin()), Ip(v.end()));

也就是说,将每个单独的 Tv 移动到 *this.如果可用,assign 可以在 *this 中重用 capacitysize.例如,如果 *this 具有与 v 相同的 size,则实现可以从 移动分配每个 Tv*this.这要求 TMoveAssignable.请注意,MoveAssignable 不需要 T 具有移动赋值运算符.复制赋值运算符也足够了.MoveAssignable 只是意味着 T 必须可从右值 T 分配.

That is, move each individual T from v to *this. The assign can reuse both capacity and size in *this if available. For example if *this has the same size as v the implementation can move assign each T from v to *this. This requires T to be MoveAssignable. Note that MoveAssignable does not require T to have a move assignment operator. A copy assignment operator will also suffice. MoveAssignable just means T has to be assignable from an rvalue T.

如果*thissize不够,则必须在*this中构造新的T代码>.这要求 TMoveInsertable.对于我能想到的任何理智的分配器,MoveInsertable 归结为与 MoveConstructible 相同的东西,这意味着可从右值 T 构造(不暗示存在 T 的移动构造函数.

If the size of *this is not sufficient, then new T will have to be constructed in *this. This requires T to be MoveInsertable. For any sane allocator I can think of, MoveInsertable boils down to the same thing as MoveConstructible, which means constructible from an rvalue T (does not imply the existence of a move constructor for T).

在第三种情况下,移动的 vector 通常不会为空.它可能充满移动元素.如果元素没有移动构造函数,则这可能等同于复制赋值.然而,没有任何东西要求这样做.如果他愿意,实现者可以自由地做一些额外的工作并执行 v.clear(),将 v 留空.我不知道有任何实现这样做,也不知道实现这样做的任何动机.但我没有看到任何禁止它.

In case Three, the moved-from vector will in general not be empty. It could be full of moved-from elements. If the elements don't have a move constructor, this could be equivalent to a copy assignment. However, there is nothing that mandates this. The implementor is free to do some extra work and execute v.clear() if he so desires, leaving v empty. I am not aware of any implementation doing so, nor am I aware of any motivation for an implementation to do so. But I don't see anything forbidding it.

David Rodríguez 报告说,在这种情况下,GCC 4.8.1 调用了 v.clear(),而将 v 留空.libc++ 没有,让 v 不为空.两种实现都是一致的.

David Rodríguez reports that GCC 4.8.1 calls v.clear() in this case, leaving v empty. libc++ does not, leaving v not empty. Both implementations are conforming.

这篇关于移动的向量总是空的吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-01 10:20