基于向量获取范围内的项目索引 [英] Obtaining item index in ranged based for on vector

查看:77
本文介绍了基于向量获取范围内的项目索引的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

C ++ 11引入了基于范围的for循环,该循环使用(const)迭代器在内部实现,因此:

The C++11 introduced ranged-based for loop that is internally implemented using (const) iterators so this:

std::vector<std::string> vec;

for(std::string &str : vec)
{
//...
}

基本上等同于更冗长(是的,可以使用 auto 进行简化):

is basically equivalent to more verbose (yes, it could be simplified using auto):

for(std::vector<std::string>::iterator it = vec.begin(); it != vec.end(); ++it)
{
//...
}

但是,通常情况下,也需要该项目的索引。使用第二种方法很简单:

However commonly one needs an index of the item as well. With the second approach that is easy:

auto index = it - vec.begin();

在基于范围的中对于如此简单。但是我想知道这是否可行且可移植的解决方案,它完全避免了迭代器:

In ranged-based for it is not so straightforward. But I was wondering if this was ok and portable solution that avoids iterators altogether:

for(auto &str : vec)
{
    auto index = &str - &vec[0];
}

const 版本将会是相同的,但是需要注意不要将非 const 容器与const引用混合使用,这可能并不总是很明显。)

(const version will be the same but one needs to watch out not to mix non-const container with const reference which might not always be obvious.)

显然,这取决于几个假设:

Obviously this relies on several assumptions:


  • vector的迭代器只是对一个项目的引用(

  • that iterator of vector is just a reference to an item (probably in the standard?)

容器被保证是连续的( std :: vector 是... )

container is guaranteed contiguous (std::vector is...)

ranged for的内部实现(也可能在标准中)

the internal implementation of ranged based for (also probably in the standard)

推荐答案

是的,但是我将使用 vec.data()代替。使用 .data()的好处是非连续的 std 容器没有它,因此您的代码当要遍历的容器无法正常工作时,可靠地停止编译(例如 deque std :: vector< bool> )。 (还有其他一些次要的优点,例如 std :: addressof 问题,事实是它在空容器中定义良好,但在这里并不是特别重要。)

Yes, but I'd use vec.data() instead. A bonus of using .data() is that non-contiguous std containers don't have it, so your code reliably stops compiling when the container being iterated over doesn't work that way (like deque or std::vector<bool>). (There are other minor advantages, like std::addressof issues, and the fact it is well defined on empty containers, but those aren't as important especially here.)

或者,我们编写 index_t 类似于迭代器的包装器:

Alternatively we write an index_t iterator-like wrapper:

template<class T>
struct index_t {
  T t;
  T operator*()const{ return t; }
  void operator++() { ++t; }
  friend bool operator==( index_t const& lhs, index_t const& rhs ) {
    return lhs.t == rhs.t;
  }
  friend bool operator!=( index_t const& lhs, index_t const& rhs ) {
    return lhs.t != rhs.t;
  }
};
template<class T>
index_t<T> index(T t) { return {t}; }

index_t< int> 创建计数 for(:)循环。

index_t< iterator> 可用于创建返回迭代器的 for(:)循环。

index_t<iterator> can be used to create iterator-returning for(:) loops.

template<class It>
struct range_t {
  It b,e;
  It begin() const {return b;}
  It end() const {return e;}
};
template<class It>
range_t<It> range( It s, It f ) { return {s,f}; }

template<class T>
range_t<index_t<T>>
index_over( T s, T f ) {
  return {{{s}}, {{f}}};
}
template<class Container>
auto iterators_of( Container& c ) {
  using std::begin; using std::end;
  return index_over( begin(c), end(c) );
}

我们现在可以在容器的迭代器上进行迭代。

we can now iterator over iterators of a container.

for (auto it : iterators_of(vec))

实时示例

上面提到的整数迭代是:

The mentioned iterate-over-integers is:

for (int i : index_over( 0, 100 ) )

我们还可以直接获取容器的索引:

we can also directly get the indexes of the container:

template<class Container>
range_t< index_t<std::size_t> >
indexes_of( Container& c ) {
  return index_over( std::size_t(0), c.size() );
}
template<class T, std::size_t N>
range_t< index_t<std::size_t> >
indexes_of( T(&)[N] ) {
  return index_over( std::size_t(0), N );
}

这可以让我们:

for( auto i : indexes_of( vec ) )

其中 i 0 vec.size()-1 。我发现有时使用它比使用zip迭代器等更容易。

where i varies from 0 to vec.size()-1. I find this is easier to work with sometimes than a zip iterator or the like.

省略了改进:

使 index_t 成为真实的 input_iterator 。根据需要使用 std :: move 和/或 std :: forward 来创建索引和范围。在范围上支持哨兵。使 range_t 界面更丰富( size ,可选的随机访问 [] range_t range_t :: without_front(n)const ,等等。

Make index_t a real input_iterator. Use std::move and/or std::forward as needed in making indexes and ranges. Support Sentinals on ranges. Make range_t interface richer (size, optional random-access [], empty, front, back, range_t range_t::without_front(n) const, etc.

这篇关于基于向量获取范围内的项目索引的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆