我来晚了,并提供了一个额外的答案,因为我不相信此时任何其他答案是完全正确的。
问题:
移出向量总是空的吗?
回答:
通常,但不,并非总是如此。
血腥细节:
vector
没有像某些类型那样的标准定义的移出状态(例如,在移出后被unique_ptr
指定为等于)。nullptr
然而,要求vector
是这样的,没有太多的选择。
答案取决于我们谈论vector
的是移动构造函数还是移动赋值运算符。在后一种情况下,答案还取决于vector
' 分配器。
vector<T, A>::vector(vector&& v)
此操作必须具有恒定的复杂性。v
这意味着除了从构建中窃取资源之外别无选择*this
,并v
处于空状态。无论分配器A
是什么,无论类型T
是什么,这都是正确的。
所以对于移动构造函数,是的,被移动的vector
总是空的。这没有直接指定,但不属于复杂性要求,并且没有其他方法可以实现它。
vector<T, A>&
vector<T, A>::operator=(vector&& v)
这要复杂得多。主要有3种情况:
一:
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
之前。
注意:在 C++11 中propagate_on_container_move_assignment
forstd::allocator
是false_type
,但这已更改为true_type
for C++1y (我们希望 y == 4)。
在情况一中,搬出者vector
将始终为空。
二:
allocator_traits<A>::propagate_on_container_move_assignment::value == false
&& get_allocator() == v.get_allocator()
(propagate_on_container_move_assignment
计算结果为false_type
,并且两个分配器比较相等)
在这种情况下,移动赋值运算符的行为类似于案例一,但有以下例外:
- 分配器没有移动分配。
- 这个案例和案例三之间的决定发生在运行时,案例三需要更多的
T
,因此案例二也是如此,即使案例二实际上并没有执行这些额外的要求T
。
在第二种情况下,搬出者vector
将始终为空。
三:
allocator_traits<A>::propagate_on_container_move_assignment::value == false
&& get_allocator() != v.get_allocator()
(propagate_on_container_move_assignment
计算结果为false_type
,并且两个分配器比较不相等)
在这种情况下,实现不能移动分配器,也不能将任何资源从v
到转移*this
(资源是内存缓冲区)。在这种情况下,实现移动赋值运算符的唯一方法是有效地:
typedef move_iterator<iterator> Ip;
assign(Ip(v.begin()), Ip(v.end()));
也就是说,将每个人T
从移动v
到*this
。assign
可以重复使用两者capacity
,如果可用的话size
。*this
例如,如果*this
具有相同size
的v
实现可以将分配每个T
从移动v
到*this
。这T
需要MoveAssignable
. 请注意,MoveAssignable
不需要T
具有移动赋值运算符。复制赋值运算符也足够了。 MoveAssignable
只是意味着T
必须可以从 rvalue 分配T
。
如果size
of*this
不充分,则T
必须在 中构建new *this
。这T
需要MoveInsertable
. 对于我能想到的任何理智的分配器,MoveInsertable
归结为与 相同的东西MoveConstructible
,这意味着可以从右值构造T
(并不意味着存在移动构造函数T
)。
在情况三中,搬家vector
一般不会是空的。它可能充满了移动的元素。如果元素没有移动构造函数,这可能等同于复制赋值。但是,没有什么可以强制执行此操作。v.clear()
如果他愿意,实现者可以自由地做一些额外的工作并执行,v
留空。我不知道有任何实现这样做,我也不知道实现这样做的任何动机。但我没有看到任何禁止它的东西。
v.clear()
David Rodríguez 报告说,在这种情况下GCC 4.8.1 调用,v
留空。 libc++没有,v
不留空。两种实现都是一致的。