为什么std :: vector需要移动构造函数用于其元素?

时间:2015-05-08 01:20:46

标签: c++ c++11 vector stl move-semantics

C ++ 98声明std :: vector元素应该有copy-constructors。在C ++ 11中,情况不再如此。相反,元素必须具有move-constructors。

根据你对std :: vector的处理方式,你可能真的需要或者可能不需要调用copy-或move-构造函数,但标准中始终只需要其中一个。为什么呢?

更新:显然,前提是不正确的。我的困惑源于阅读this等答案。

2 个答案:

答案 0 :(得分:5)

正如评论中所暗示的那样,对于存储在向量中的所有对象,并不是简单的一组要求。相反,要求是针对特定的操作。

虽然许多操作需要的对象类型为MoveConstructible和/或MoveAssignable,但复制构造和副本分配符合满足该要求的要求 - 即,copy constructible基本上是移动可构造的超集(同样是复制赋值与移动赋值)。

但事实并非如此。例如,如果对向量使用两个参数构造函数:

std::vector<T> vec(n, t);

...然后T必须是可复制的(因为它将尝试在向量中创建nt个副本,并且通过移动构造,它只能够创建一个项目。)

如果要将其视为继承层次结构,可以将copy(赋值|构造)视为派生类,并将(赋值|构造)作为基类移动,因此可以隐式替换复制以进行移动但反之亦然。

在很多情况下,list元素的要求比vectordeque元素的要求更宽松(对vector元素的要求和deque几乎总是相同的,唯一的例外是我(感谢@yakk)是emplace_back,这需要MoveConstructible用于vector,EmplaceConstructible以及下面列出的下一个项目 - 但请注意需要注意的)。

如果使用std::vector<x> myvector(i, j);myvector.assign(i, j);,(其中ij是迭代器),则对T的要求取决于迭代器的类 - 如果它们'重新转发迭代器,然后T只需要是EmplaceConstructible,否则T必须是MoveConstructible。警告:虽然这确实反映了标准中的当前措辞,但根据LWG 2266,构造函数的限制是不正确的:它们应该适用于迭代器类别,并且同样适用于vector和deque。该标准的某些未来版本将反映出这一点,但实际使用的情况就是这样。

如果有人关心为什么会这样,以及为什么对非前向迭代器进行特殊处理:问题是像std::istream_iterator这样的问题,就没有办法提前知道迭代器有多少项范围可能是指。使用随机访问迭代器之类的东西,它可以简单地使用j - i来确定项目数,创建那么多空间并适当插入。为了有效地使用istream_iterator s,它们通常会将数据复制到当前集合的末尾,然后在将整个范围复制/移动到集合中之后,使用rotate将它们移动到所需的集合中地点。使用rotate的额外要求支持。

如果你要插入insert或{{1},那么vector的一个变体除了(Move | Copy)(Constructible | Assignable)之外还需要元素可以交换。 (这里的推理类似于上面给出的推理:对于deque的这个变体,元素实际上插在一个地方,然后四处移动以使它们到达它们所属的位置。

答案 1 :(得分:0)

它在哪里声明元素必须在C ++ 11中具有std :: vector的move-constructors?像这样的规则会破坏很多现有代码,因为现有的类不一定具有移动构造函数。您可能需要复制构造函数,或者如果您没有复制构造函数,那么您至少需要一个移动构造函数。