c++ - 为什么 std::vector 的元素需要 move 构造函数?

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

C++98 声明 std::vector 元素应该有复制构造函数。在 C++11 中,情况不再如此。相反,元素必须具有 move 构造函数。

根据您对 std::vector 所做的操作,您可能真的需要也可能不需要调用复制构造函数或 move 构造函数,但标准始终只正式要求调用其中之一。为什么?

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

最佳答案

正如评论中所暗示的那样,对于存储在 vector 中的所有对象,并没有简单的一揽子要求。相反,要求放在特定操作上。

虽然一些操作确实需要对象类型是MoveConstructible和/或 MoveAssignable ,复制构造和复制赋值符合该要求——也就是说,复制构造基本上是 move 构造的超集和(同样复制赋值与 move 赋值)。

然而,反之则不然。例如,如果您对 vector 使用两个参数的构造函数:

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

...然后T必须是可复制构造的(因为它将尝试在 vector 中创建 nt 拷贝,并且使用 move 构造,它只能创建一个项)。

如果你想把它看成一个继承层次,你可以把copy(assignment|construction)看成派生类,move(assignment|construction)看成基类,这样你就可以隐式地用复制代替 move 但反之则不然。

在很多情况下,对 list 元素的要求比 vector 元素上的那些更宽松或 deque (对 vectordeque 的元素的要求几乎总是相同的,我知道(感谢@yakk)的唯一异常(exception)是 emplace_back ,它需要 vector 的 MoveConstructible,否则需要 EmplaceConstructible 和下面列出的下一项--但请注意它的警告)。

如果您使用 std::vector<x> myvector(i, j);myvector.assign(i, j); , (其中 ij 是迭代器)那么对 T 的要求取决于迭代器的类——如果它们是前向迭代器,那么 T 只需要是 EmplaceConstructible,否则 T 必须是 MoveConstructible。警告:虽然这确实反射(reflect)了标准中的当前措辞,但根据 LWG 2266对构造函数的限制是不正确的:无论迭代器类别如何,它们都应该适用,并且同样适用于 vector 和 deque。该标准的某些 future 版本将反射(reflect)这一点,但在实际使用中已经是这样了。

万一有人关心为什么会这样,以及为什么对非前向迭代器进行特殊处理:问题是像 std::istream_iterator 这样的东西,无法提前知道迭代器范围可能引用多少项。使用随机访问迭代器之类的东西,它可以简单地使用 j - i确定项目的数量,留出足够的空间,然后适本地插入。使用 istream_iterator 高效工作s,他们通常将数据复制到当前集合的末尾,然后在整个范围被复制/move 到集合中后,使用 rotate将它们 move 到所需位置。额外要求支持使用 rotate .

insert 有一种变体如果您要插入到 vector 中,则除了 (Move|Copy)(Constructible|Assignable) 之外还需要元素可交换或 deque (这里的推理与上面给出的推理类似:对于 insert 的这种变体,元素实际上被插入到一个地方,然后四处 move 以使它们到达它们所属的位置)。

关于c++ - 为什么 std::vector 的元素需要 move 构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30114291/

相关文章:

c++ - 如何告诉c++编译器对象在别处没有改变以达到更好的优化

c++ - 将 vera++(或其他编码约定工具)与 Qt/QtCreator 集成

c++ - 内存分配失败

c++11 - 枚举 : int and enum : const int 之间有什么区别吗

c++ - 多个线程访问共享资源

c++ - C++ 中的 3D 运动场

c++ - 使用 phoenix::bind 将 spirit 解析的结构传递给函数

c++ - 如何初始化 std::array<std::array<T, 2>, 2> 的对象?

c++ - 将集合转换为 vector 的 vector

c++ - 在 std::move() 之后删除堆上的结构成员