修剪连续的标准容器

Trimming a continous std container

假设我有一个类似于以下内容的容器:

std::vector<int> numbers{1,2,3,4,5,6,7,8};

什么是最快捷的方法"trim"呢?就像从中删除元素一样,但只能从开头或结尾开始。

假设我想将 'numbers' 转换为容器 {3,4,5,6,7}。我能想到的一种非常有效地删除“8”的方法是:

numbers.resize(numbers.size()-2);

这似乎保证不会重新分配并删除所有不适合新大小的尾随元素(在本例中仅最后一个元素 8)。

在容器的开头有没有类似的方法?而且,只要我传递给调整大小的参数小于或等于容器的原始大小,这个操作就保证是 O(1) 吗?

std::vector 不支持有效地从前面删除元素,但 boost::circular_buffer 支持:http://www.boost.org/doc/libs/release/doc/html/circular_buffer.html

在某种程度上,std::deque也支持这个,但是它有一些问题,比如常见的实现一次只分配少量内存,这意味着它可能比[=分配更多次内存13=]。从 std::deque 中删除许多元素并没有那么快,因为它必须释放许多块(而 circular_buffer 只是增加一个整数值)。

One approach I can think of to remove the '8' quite efficiently is:

numbers.resize(numbers.size()-2);

假设这应该是 -1,不,那不是你想要做的。您应该始终选择适合工作的工具。在这种情况下,如果您只想删除最后一个元素,那就是:

numbers.pop_back();

如果你想删除最后的 n 个元素(假设整个 n <= numbers.size() 个),那就是:

numbers.erase(numbers.end() - n, numbers.end());

这保证没有重新分配或额外的移动 - 它只会调用适当的析构函数,然后移动结束指针。如果类型是微不足道的可破坏的,那么即使是第一部分也是空操作。

如果要删除第一个 n个元素,那就是对称的:

numbers.erase(numbers.begin(), numbers.begin() + n);

然而,这涉及到移动所有后面的元素来填充这个洞——所以它是剩下多少元素的函数。 vector 从背面擦除成本低,但从正面擦除成本高(这本身就是拥有 pop_back() 而不是 pop_front() 的动机)。因此,如果你想trim(),先从右边擦除。

如果您要从正面进行大量擦除,则应考虑使用擅长从两端 trim 进行擦除的容器 - 如 std::deque。好的是——无论哪种方式,代码看起来都一样。您仍然想使用双迭代器 erase() 到 trim。

你肯定不想做的是(尽管它看起来多么诱人):

numbers.assign(numbers.begin() + 2, numbers.end() - 1);

这是未定义的行为。