如何遍历 std::vector 的范围

How to iterate over ranges of a std::vector

提问人:RAM 提问时间:11/18/2023 最后编辑:Jarod42RAM 更新时间:11/18/2023 访问量:115

问:

我有一个关于使用 C++11 或 C++14 迭代向量切片/范围的最佳方法是什么的问题。假设我们有一个包含一堆值的:std::vector

std::vector<int> v = {0,1,2,3,4,5,6,7,8,9,10};

此向量用作输入,以根据连续值计算值,其操作类似于移动平均线。为此,我有一个函数,该函数将连续元素作为输入,进行一些数学运算,并输出一个值。这意味着此函数类似于以下声明:nfoon

int foo(const std::vector<int> &v_range);

我想应用于所有元素,将它们映射到另一个向量。 我可以通过遍历向量,提取子向量并将其传递给 .请参阅下面的代码。foovfoo

// Example program
#include <vector>
#include <iostream>

int foo(const std::vector<int> &v) {
    
    std::cout << "[";
    for(int e: v) {
        std::cout << e << ",";
    }
    std::cout << "]" << std::endl;
    return 0;
}

int main()
{
  std::vector<int> v = {0,1,2,3,4,5,6,7,8,9,10};
  std::vector<int> v_out;
  std::vector<int> v = {0,1,2,3,4,5,6,7,8,9,10};
  std::vector<int> v_out;
  const int n = 3;

  for (auto begin = v.begin(), end = std::next(v.begin(), n);
       std::next(begin, n - 1) != v.end();
       ++begin, ++end) {
      std::vector<int> v_slice(begin, end);
      v_out.push_back(foo(v_slice));
  }
}

演示

上述方法有效,但它不仅需要大量样板代码,而且还不必要地复制元素。

我想知道 C++ 是否提供了任何巧妙的方法来轻松迭代向量元素的切片/范围,这些元素可以传递给 或 。std::transformstd::for_each

或者,我想知道 C++ 是否提供了任何将输入向量转换为向量范围向量的方法,类似于以下伪代码:

  std::vector<int> v = {0,1,2,3,4,5,6,7,8,9,10};


  std::vector<std::vector<int>> v_sliced = { 
    {0,1,2},
    {1,2,3},
    {2,3,4}
    ///...
    {8,9,10}
  };

欢迎任何意见。

C++ 向量 迭代 C++14

评论

3赞 Jarod42 11/18/2023
你能改变取范围(C++20),迭代器对,(C++20)吗?foostd::span
1赞 RAM 11/18/2023
@Jarod42 我很抱歉不清楚,但这个问题是关于 C++11 或 C++14 的。这意味着,像 C++20 的范围库这样的东西不适用。
1赞 273K 11/18/2023
end++ != v.end() - v.end()++是UB
3赞 Jarod42 11/18/2023
仍然有一对迭代器。(这至少可以避免复制/创建额外的向量)
3赞 273K 11/18/2023
为什么不使用和摆脱?int foo(std::vector<int>::iterator begin, std::vector<int>::iterator end);v_slice

答:

1赞 Jarod42 11/18/2023 #1

有了 ,您可能有:std::transformstd::vector<std::vector<int>>

std::vector<std::vector<int>> sliced_n(const std::vector<int>& v, std::size_t n)
{
    if (v.size() < n) throw std::invalid_argument("");

    std::vector<std::vector<int>> res;

    std::transform(v.begin(), v.end() - n + 1,
                   std::back_inserter(res),
                   [n](auto &e){ return std::vector(&e, &e + n); });
    return res;
}

int main()
{
  std::vector<int> v = {0,1,2,3,4,5,6,7,8,9,10};
  auto sliced = sliced_n(v, 3);
  std::vector<int> v_out;
  std::transform(sliced.begin(), sliced.end(),
                 std::back_inserter(v_out),
                 [](const auto& v){ return foo(v); });
}

演示

但更简单

std::transform(v.begin(), v.end() + 1 - n,
               std::back_inserter(v_out),
               [&](const auto& e){ return foo(std::vector(&e, &e + n)); });

演示

评论

0赞 RAM 11/18/2023
如前所述,您的示例已损坏,因为它无法遍历。{8,9,10}
0赞 Jarod42 11/18/2023
偏移固定...