【问题标题】:Is the capacity required to be preserved when moving a std::vector?移动 std::vector 时是否需要保留容量?
【发布时间】:2012-10-19 05:10:25
【问题描述】:

考虑以下代码:

std::vector vec;
vec.reserve(500);
size_t cap = vec.capacity();

std::vector newVec = std::move(vec);
assert(cap == newVec.capacity());

在您遇到的几乎任何实现中,这都会起作用。我不在乎实现是做什么的。我想知道标准需要什么。移至vector 的容量是否与原始容量相同?还是断言会触发?

【问题讨论】:

  • 如果容量发生变化,那将完全破坏首先具有移动语义的动机,即避免不必要的复制和重新分配。不过我手头没有标准,所以不能给你正确的答案。

标签: c++ c++11 stdvector language-lawyer move-semantics


【解决方案1】:

查看标准,移动构造函数似乎不需要任何东西,但是正如@amaurea 所说,如果移动构造函数尝试分配或释放内存,它将完全违背移动语义的目的,所以我会期望容量在所有实现中保持不变。


23.2.1 一般容器要求

表达式

X u(a);
X u = a;

断言/注释前置/后置条件

要求:T 可复制插入到 X 中(见下文)。
发帖:u == a


标准只要求newVec == vec。由于std::vector::operator== 没有考虑容量,newVec 不一定与vec 具有相同的容量。

【讨论】:

  • 你答案的第一段与你在最后一段中的警告相矛盾,所以很难说你的断言更强烈。
【解决方案2】:

std::vector 的移动构造函数的 C++11 标准要求是(表 99 — 分配器感知容器要求):

X(rv)
X u(rv)
  • 分配器的移动构造不应通过异常退出
  • post: u 应与rv 在此构造之前具有相同的元素; get_allocator() 的值应与构造前rv.get_allocator() 的值相同。
  • 复杂性:恒定

这里对容量没有要求/保证。但是我们可以得出结论,恒定的复杂性隐含地否认了任何重新分配。除了重新分配之外,我看不到任何其他改变容量的逻辑理由。所以应该是一样的。

从另一个角度来看,如果移动的向量是空的,那么忽略它并默认构造自身是完全合法的。这仍然是 O(1),因为它不需要任何每个元素的构造。 (感谢Nicol Bolas 提供此问题)。

还可以使用std::allocator::allocate 函数的hint 参数将容量缩小到大小:

pointer allocate(size_type, allocator<void>::const_pointer hint = 0);

hint 的使用未指定,但如果实现需要,则旨在帮助本地化。所以一些复杂的解决方案可能可以将向量存储指针传递为hint,并在其上使用realloc来缩小容量。

结论:看起来标准并不能保证在移动std::vector时保持容量,存储空间可能会缩小。

【讨论】:

  • "但我们可以得出结论,恒定的复杂性隐含地否认任何重新分配。" +1
  • 似乎实现可以只是将容量减小到大小并仍然满足所有法律要求,尽管这将是毫无意义的浪费。
  • @aschepler:但是std::allocator&lt;&gt; 接口的哪一部分可以在幕后使用realloc
  • @Rost:这里的问题是,因为vec 是空的,所以newVec 忽略空的移动对象和默认构造本身是完全合法的。这仍然是 O(1),因为它不需要任何每个元素的构造。
  • @Peter Alexander 哦,我终于看到了。可是等等!在堆栈上分配和通过::operator new(size_t) 分配是不同的可观察行为!
猜你喜欢
  • 2012-12-13
  • 1970-01-01
  • 2014-02-18
  • 1970-01-01
  • 2019-03-12
  • 1970-01-01
  • 2018-10-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多