在 C++11 foreach 循环中获取索引
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24881799/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Get index in C++11 foreach loop
提问by hildensia
Is there a convenient way to get the index of the current container entry in a C++11 foreach loop, like enumerate
in python:
是否有一种方便的方法可以在 C++11 foreach 循环中获取当前容器条目的索引,例如enumerate
在 python 中:
for idx, obj in enumerate(container):
pass
I could imagine an iterator that can also return the index or similar.
我可以想象一个迭代器也可以返回索引或类似的。
Of course I could have a counter, but often iterators don't give guarantees of the order they iterate over a container.
当然我可以有一个计数器,但迭代器通常不保证它们在容器上迭代的顺序。
采纳答案by CK1
A good implementation of the feature you are requested can be found here:
可以在此处找到您请求的功能的良好实现:
https://github.com/ignatz/pythonic
https://github.com/ignatz/pythonic
The idea behind is, that you build a wrapper struct with a custom iterator that does the counting. Below is a very minimal exemplary implementation to illustrate the idea:
背后的想法是,您使用自定义迭代器构建一个包装结构来进行计数。下面是一个非常小的示例性实现来说明这个想法:
#include <iostream>
#include <vector>
#include <tuple>
// Wrapper class
template <typename T>
class enumerate_impl
{
public:
// The return value of the operator* of the iterator, this
// is what you will get inside of the for loop
struct item
{
size_t index;
typename T::value_type & item;
};
typedef item value_type;
// Custom iterator with minimal interface
struct iterator
{
iterator(typename T::iterator _it, size_t counter=0) :
it(_it), counter(counter)
{}
iterator operator++()
{
return iterator(++it, ++counter);
}
bool operator!=(iterator other)
{
return it != other.it;
}
typename T::iterator::value_type item()
{
return *it;
}
value_type operator*()
{
return value_type{counter, *it};
}
size_t index()
{
return counter;
}
private:
typename T::iterator it;
size_t counter;
};
enumerate_impl(T & t) : container(t) {}
iterator begin()
{
return iterator(container.begin());
}
iterator end()
{
return iterator(container.end());
}
private:
T & container;
};
// A templated free function allows you to create the wrapper class
// conveniently
template <typename T>
enumerate_impl<T> enumerate(T & t)
{
return enumerate_impl<T>(t);
}
int main()
{
std::vector<int> data = {523, 1, 3};
for (auto x : enumerate(data))
{
std::cout << x.index << ": " << x.item << std::endl;
}
}
回答by Adrian Maire
What about a simple solution like:
一个简单的解决方案怎么样:
int counter=0;
for (auto &val: container)
{
makeStuff(val, counter);
counter++;
}
You could make a bit more "difficult" to add code after the counter by adding a scope:
您可以通过添加范围使在计数器后添加代码更加“困难”:
int counter=0;
for (auto &val: container)
{{
makeStuff(val, counter);
}counter++;}
As @graham.reeds pointed, normal for
loop is also a solution, that could be as fast:
正如@graham.reeds 所指出的,正常for
循环也是一种解决方案,它可以同样快:
int counter=0;
for (auto it=container.begin(); it!=container.end(); ++it, ++counter)
{
makeStuff(val, counter);
}
And finally, a alternative way using algorithm:
最后,另一种使用算法的方法:
int counter = 0;
std::for_each(container.begin(), container.end(), [&counter](int &val){
makeStuff(val, counter++);
});
Note: the order between range loop and normal loop is guaranteed by the standard 6.5.4. Meaning the counter is able to be coherent with the position in the container.
注意:范围循环和普通循环之间的顺序由标准 6.5.4 保证。这意味着计数器能够与容器中的位置保持一致。
回答by Zitrax
If you have access to Boost it's range adaptors can be used like this:
如果您可以访问 Boost,则可以像这样使用它的范围适配器:
using namespace boost::adaptors;
for (auto const& elem : container | indexed(0))
{
std::cout << elem.index() << " - " << elem.value() << '\n';
}
Source(where there are also other examples)
来源(这里还有其他例子)
回答by underscore_d
C++17 and structured bindings makes this look OK - certainly better than some ugly mutable lambda with a local [i = 0](Element&) mutable
or whatever I've done before admitting that probably not everything should be shoehorned into for_each()
et al.- and than other solutions that require a counter with scope outside the for
loop.
C++17 和结构化绑定使这看起来不错 - 当然比一些带有本地的丑陋可变 lambda[i = 0](Element&) mutable
或者我在承认可能不是所有东西都应该硬塞进for_each()
等人之前所做的任何事情都要好。- 而不是其他需要一个范围在for
循环外的计数器的解决方案。
for (auto [it, end, i] = std::tuple{container.cbegin(), container.cend(), 0};
it != end; ++it, ++i)
{
// something that needs both `it` and `i`ndex
}
You could make this generic, if you use this pattern often enough:
如果您经常使用此模式,则可以使其通用:
template <typename Container>
auto
its_and_idx(Container&& container)
{
using std::begin, std::end;
return std::tuple{begin(container), end(container), 0};
}
// ...
for (auto [it, end, i] = its_and_idx(foo); it != end; ++it, ++i)
{
// something
}
C++ Standard proposal P2164proposes to add views::enumerate
, which would provide a view of a range giving both reference-to-element and index-of-element to a user iterating it.
C++ 标准提案P2164建议添加views::enumerate
,这将提供一个范围的视图,为迭代它的用户提供元素引用和元素索引。
We propose a view
enumerate
whose value type is astruct
with 2 membersindex
andvalue
representing respectively the position and value of the elements in the adapted range.[ . . .]
This feature exists in some form in Python, Rust, Go (backed into the language), and in many C++ libraries:
ranges-v3
,folly
,boost::ranges
(indexed
).The existence of this feature or lack thereof is the subject of recurring stackoverflow questions.
我们提出了一个视图,
enumerate
其值类型为 a,struct
有 2 个成员index
,value
分别表示适应范围内元素的位置和值。[ . . .]
此功能以某种形式存在于 Python、Rust、Go(支持该语言)以及许多 C++ 库中:
ranges-v3
,folly
,boost::ranges
(indexed
)。此功能是否存在是反复出现的 stackoverflow 问题的主题。
Hey, look! We're famous.
你看!我们很有名。
回答by graham.reeds
If you need the index then a traditional for works perfectly well.
如果您需要索引,那么传统的 for 工作得很好。
for (int idx=0; idx<num; ++idx)
{
// do stuff
}