【问题标题】:Iterator for a subset of a vector向量子集的迭代器
【发布时间】:2015-08-12 22:35:23
【问题描述】:

是否有可能从一个向量中得到一个const迭代器,该迭代器在失效之前只能迭代向量的某个范围?

例如,如果我有一个包含 10 个元素的向量,我想返回一个包含 4 到 7 个元素的迭代器。

伪代码:

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

    auto iterator = GetRangedIterator(vector, 4, 7)
    for (const int& num : iterator)
        print num;      // 4, 5, 6, 7
}

【问题讨论】:

  • 迭代器不会变得“无效”,它们只是变得等于结束迭代器。由于您可以随意使用任何您想要的迭代器作为结尾,这很简单。

标签: c++ c++11 vector range


【解决方案1】:

这很简单(尽管我将结果称为范围,而不是迭代器)。

一个简单的实现如下所示:

template <class Iter>
class range {
    Iter b;
    Iter e;
public:

    range(Iter b, Iter e) : b(b), e(e) {}

    Iter begin() { return b; }
    Iter end() { return e; }
};

template <class Container>
range<typename Container::iterator> 
make_range(Container& c, size_t b, size_t e) {
    return range<typename Container::iterator> (c.begin()+b, c.begin()+e);
}

就目前而言,这遵循正常的 C++ 约定(基于 0 的计数,您指定的结尾超出范围的结尾,而不是在其中),因此要获得您要求的输出,您需要指定3, 7 的范围,例如:

for (int num : make_range(vector, 3, 7))
    std::cout << num << ", ";      // 4, 5, 6, 7,

请注意,基于范围的 for 循环知道如何使用 beginend 成员函数来告诉它要迭代的范围,因此我们不必处理无效的迭代器或类似的事情那,我们只需要指定我们关心的范围的开始和结束。

【讨论】:

  • 我看到的唯一缺点是begin()+x 不是Container 的有效操作,例如std::list。因此,您需要花费大量时间来确定范围的界限,然后才能使用它。例如,std::vector 不会受此影响。
  • @RemyLebeau:幸运的是,begin()+x 不会为 std::list 或任何其他没有随机访问迭代器的容器编译。
  • 优秀的答案。我会为beginend 添加const 重载。
  • 我喜欢这种方法,非常好。以前总是使用 boost,但很高兴有一种使用纯 STL 的方法。
【解决方案2】:

您可以使用range-v3 库,它是will been part of C++20 的 Ranges TS 的基础,但该库已经与 C++11 编译器一起使用。方法如下:

#include <range/v3/all.hpp>
#include <iostream>
#include <vector>

int main() 
{
    using namespace ranges;

    auto v = view::iota(1, 11) | to_<std::vector<int>>();
    std::cout << view::all(v)  << '\n';

    auto rng = v | view::slice(3, 7); 
    std::cout << rng << '\n';
}

Live Example.

【讨论】:

  • 我建议你修改你的例子,从 OP 的向量明确开始;您的代码现在令人困惑的方式,所以我相信,认为使用 iotato 的新手可能是获取迭代器而不是设置测试的解决方案的一部分。
  • auto的数据类型是什么
【解决方案3】:

你可以用这个

for( it = v1.begin() + a; it <= v1.begin() + b; it++ )

下面的代码是一个小演示

#include <vector>
#include <iostream>

using namespace std;

int main(){

  vector<int> v1;

  for( int i = 0; i < 50; i++ ){
    v1.push_back( 2*(i+1) );
    cout<<v1.at(i)<<"   ";
  }
  cout<<endl;


  vector<int>::iterator it;

  int a = 5;
  int b = 9;

  for( it = v1.begin() + a; it <= v1.begin() + b; it++ ){

    cout<<(*it)<<"   ";
  }
  cout<<endl; 
}

输出是

2   4   6   8   10   12   14   16   18   20   22   24   26   28   30   32   34   36   38   40   42   44   46   48   50   52   54   56   58   60   62   64   66   68   70   72   74   76   78   80   82   84   86   88   90   92   94   96   98   100   
12   14   16   18   20   

【讨论】:

    【解决方案4】:

    标准库中没有明确地执行此操作,但如果您愿意使用 Boost,则可以使用以下方法使用 range 概念:

    auto range = boost::make_iterator_range(v.begin()+3, v.begin()+7);
    
    BOOST_FOREACH(int i, range)
    {
        cout << i << endl;
    }
    

    这应该输出4567

    【讨论】:

      【解决方案5】:

      您可以使用span

      auto first_index = 3;
      auto last_index = 6;
      auto count = last_index - first_index + 1;
      auto interesting_nums = gsl::make_span(std::cbegin(vector) + start_index, count);
      for(auto& num : interesting_nums) { /* do stuff */ }
      

      注意事项:

      • 在您的示例中,您获得了第 4 到第 7 个元素,即我在这里使用的索引 3 到 6。
      • 这段代码几乎适用于任何容器,而不仅仅是矢量。
      • span 是 GSL 的一部分,支持 C++ Core Guidelines 库。它们可能会在 2020 年正式进入 C++ 标准,受到社区的好评。

      【讨论】:

        猜你喜欢
        • 2014-02-08
        • 1970-01-01
        • 2017-08-29
        • 2011-11-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-09
        相关资源
        最近更新 更多