在向量内移动项目的最有效方法是什么?

发布于 2024-12-06 06:46:10 字数 264 浏览 0 评论 0原文

我见过一些特殊情况,可以使用 std::rotate 或与其中一种搜索算法结合使用,但通常:当一个人有 N 个项目的向量并想要编写如下函数时

void move( int from, int count, int to, std::vector<int>& numbers );

:一直在考虑创建一个新的向量+ std::copy 或插入/擦除的组合,但我不能说我最终得到了一些漂亮而优雅的解决方案。

I've seen some special cases where std::rotate could be used or a combination with one of the search algorithms but generally: when one has a vector of N items and wants to code function like:

void move( int from, int count, int to, std::vector<int>& numbers );

I've been thinking about creation of a new vector + std::copy or combination of insert/erase but I can't say I ended up with some nice and elegant solution.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

东北女汉子 2024-12-13 06:46:10

在得出任何结论之前先进行分析总是很重要的。 vector 数据内存的连续性可能会提供基于节点的容器所没有的显着缓存优势。因此,也许您可​​以尝试直接方法:

void move_range(size_t start, size_t length, size_t dst, std::vector<T> & v)
{
  const size_t final_dst = dst > start ? dst - length : dst;

  std::vector<T> tmp(v.begin() + start, v.begin() + start + length);
  v.erase(v.begin() + start, v.begin() + start + length);
  v.insert(v.begin() + final_dst, tmp.begin(), tmp.end());
}

在 C++11 中,您可以将第一行和第三行中的迭代器包装到 std::make_move_iterator 中。

(要求 dst 不能位于 [start, start + length) 范围内,否则问题无法明确定义。)

It's always important to profile before jumping to any conclusions. The contiguity of vector's data memory may offer significant caching benefits that node-based containers don't. So, perhaps you could give the direct approach a try:

void move_range(size_t start, size_t length, size_t dst, std::vector<T> & v)
{
  const size_t final_dst = dst > start ? dst - length : dst;

  std::vector<T> tmp(v.begin() + start, v.begin() + start + length);
  v.erase(v.begin() + start, v.begin() + start + length);
  v.insert(v.begin() + final_dst, tmp.begin(), tmp.end());
}

In C++11, you'd wrap the iterators in the first and third line into std::make_move_iterator.

(The requirement is that dst not lie within [start, start + length), or otherwise the problem is not well-defined.)

猫卆 2024-12-13 06:46:10

根据向量的大小和涉及的范围,这可能比执行复制/擦除/插入更便宜。

template <typename T>
void move_range(size_t start, size_t length, size_t dst, std::vector<T> & v)
{
    typename std::vector<T>::iterator first, middle, last;
    if (start < dst)
    {
        first  = v.begin() + start;
        middle = first + length;
        last   = v.begin() + dst;
    }
    else
    {
        first  = v.begin() + dst;
        middle = v.begin() + start;
        last   = middle + length;
    }
    std::rotate(first, middle, last);
}

(这假设范围有效并且不重叠。)

Depending on the size of the vector and the ranges involved, this might be less expensive than performing copy/erase/insert.

template <typename T>
void move_range(size_t start, size_t length, size_t dst, std::vector<T> & v)
{
    typename std::vector<T>::iterator first, middle, last;
    if (start < dst)
    {
        first  = v.begin() + start;
        middle = first + length;
        last   = v.begin() + dst;
    }
    else
    {
        first  = v.begin() + dst;
        middle = v.begin() + start;
        last   = middle + length;
    }
    std::rotate(first, middle, last);
}

(This assumes the ranges are valid and they don't overlap.)

高冷爸爸 2024-12-13 06:46:10

C++11 之前的版本(尽管以下内容仍然有效),对于专门化/重载 std::swap 的包含类型,您可以获得更高效的“移动”。要利用这一点,您需要执行类似的操作

std::vector<Foo> new_vec;
Foo tmp;

for (/* each Foo&f in old_vec, first section */) {
    swap (f, tmp);
    new_vec .push_back (tmp);
}

for (/* each Foo&f in old_vec, second section */) {
    swap (f, tmp);
    new_vec .push_back (tmp);
}

for (/* each Foo&f in old_vec, third section */) {
    swap (f, tmp);
    new_vec .push_back (tmp);
}

swap (new_vec, old_vec);

。如果 Foo 有移动运算符但没有专门的 swap,那么上面的内容也可能为 C++11 提供良好的结果。

如果Foo一些巧妙的序列类型可能效果更好> 没有移动语义或其他优化的 swap

另请注意,如果上述内容在函数中

std::vector<Foo> move (std::vector<Foo> old_vec, ...)`

,那么您可能能够执行整个操作 无需复制任何内容,即使在 C++98 中要实现此功能,您需要经过valuenot 通过引用,这违背了传统的首选通过引用传递的智慧。

Pre-C++11 (although the following remains valid) you can get more efficient "moves" for contained types which specialise/overload std::swap. To take advantage of this, you would need to do something like

std::vector<Foo> new_vec;
Foo tmp;

for (/* each Foo&f in old_vec, first section */) {
    swap (f, tmp);
    new_vec .push_back (tmp);
}

for (/* each Foo&f in old_vec, second section */) {
    swap (f, tmp);
    new_vec .push_back (tmp);
}

for (/* each Foo&f in old_vec, third section */) {
    swap (f, tmp);
    new_vec .push_back (tmp);
}

swap (new_vec, old_vec);

The above may also give good results for C++11 if Foo has a move-operator but hasn't specialised swap.

Linked lists or some clever sequence type might work out better if Foo doesn't have move semantics or an otherwise-optimised swap

Note also that if the above is in a function

std::vector<Foo> move (std::vector<Foo> old_vec, ...)`

then you might be able to perform the whole operation without copying anything, even in C++98 but for this to work you will need to pass by value and not by reference, which goes against the conventional prefer-pass-by-reference wisdom.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文