基于向量获取范围内的项目索引 [英] Obtaining item index in ranged based for on vector
问题描述
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屋!