C++ 中类似 Python 的循环枚举
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11328264/
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
Python-like loop enumeration in C++
提问by betabandido
Possible Duplicate:
Find position of element in C++11 range-based for loop?
I have a vector
and I would like to iterate it and, at the same time, have access to the indexes for each individual element (I need to pass both the element and its index to a function). I have considered the following two solutions:
我有一个vector
,我想迭代它,同时可以访问每个单独元素的索引(我需要将元素及其索引都传递给一个函数)。我考虑了以下两种解决方案:
std::vector<int> v = { 10, 20, 30 };
// Solution 1
for (std::vector<int>::size_type idx = 0; idx < v.size(); ++idx)
foo(v[idx], idx);
// Solution 2
for (auto it = v.begin(); it != v.end(); ++it)
foo(*it, it - v.begin());
I was wondering whether there might be a more compact solution. Something similar to Python's enumerate. This is the closest that I got using a C++11 range-loop, but having to define the index outside of the loop in a private scope definitely seems to be like a worse solution than either 1 or 2:
我想知道是否可能有更紧凑的解决方案。类似于 Python 的enumerate。这是我使用 C++11 范围循环获得的最接近的结果,但必须在私有范围内定义循环外的索引,这似乎是比 1 或 2 更糟糕的解决方案:
{
int idx = 0;
for (auto& elem : v)
foo(elem, idx++);
}
Is there any way (perhaps using Boost) to simplify the latest example in such a way that the index gets self-contained into the loop?
有没有办法(也许使用 Boost)来简化最新的例子,使索引自包含到循环中?
采纳答案by Xeo
As @Kos says, this is such a simple thing that I don't really see the need to simplify it further and would personally just stick to the traditional for loop with indices, except that I'd ditch std::vector<T>::size_type
and simply use std::size_t
:
正如@Kos 所说,这是一件如此简单的事情,我真的不认为有必要进一步简化它,并且我个人只会坚持使用带有索引的传统 for 循环,除了我会放弃std::vector<T>::size_type
并简单地使用std::size_t
:
for(std::size_t i = 0; i < v.size(); ++i)
foo(v[i], i);
I'm not too keen on solution 2. It requires (kinda hidden) random access iterators which wouldn't allow you to easily swap the container, which is one of the strong points of iterators. If you want to use iterators and make it generic (and possibly incur a performance hit when the iterators are notrandom access), I'd recommend using std::distance
:
我不太热衷于解决方案 2。它需要(有点隐藏)随机访问迭代器,这不允许您轻松交换容器,这是迭代器的强项之一。如果您想使用迭代器并使其通用(并且当迭代器不是随机访问时可能会导致性能下降),我建议使用std::distance
:
for(auto it(v.begin()); it != v.end(); ++it)
foo(*it, std::distance(it, v.begin());
回答by Morwenn
Here is some kind of funny solution using lazy evaluation. First, construct the generator object enumerate_object
:
这是使用惰性求值的某种有趣的解决方案。首先,构造生成器对象enumerate_object
:
template<typename Iterable>
class enumerate_object
{
private:
Iterable _iter;
std::size_t _size;
decltype(std::begin(_iter)) _begin;
const decltype(std::end(_iter)) _end;
public:
enumerate_object(Iterable iter):
_iter(iter),
_size(0),
_begin(std::begin(iter)),
_end(std::end(iter))
{}
const enumerate_object& begin() const { return *this; }
const enumerate_object& end() const { return *this; }
bool operator!=(const enumerate_object&) const
{
return _begin != _end;
}
void operator++()
{
++_begin;
++_size;
}
auto operator*() const
-> std::pair<std::size_t, decltype(*_begin)>
{
return { _size, *_begin };
}
};
Then, create a wrapper function enumerate that will deduce the template arguments and return the generator:
然后,创建一个包装函数 enumerate 将推导出模板参数并返回生成器:
template<typename Iterable>
auto enumerate(Iterable&& iter)
-> enumerate_object<Iterable>
{
return { std::forward<Iterable>(iter) };
}
You can now use your function that way:
您现在可以这样使用您的功能:
int main()
{
std::vector<double> vec = { 1., 2., 3., 4., 5. };
for (auto&& a: enumerate(vec)) {
size_t index = std::get<0>(a);
double& value = std::get<1>(a);
value += index;
}
}
The implementation above is a mere toy: it should work with both const
and non-const
lvalue-references as well as rvalue-references, but has a real cost for the latter though, considering that it copies the whole iterable object several times. This problem could surely be solved with additional tweaks.
上面的实现只是一个玩具:它应该适用于const
非const
左值引用和右值引用,但考虑到它多次复制整个可迭代对象,对于后者有实际成本。这个问题肯定可以通过额外的调整来解决。
Since C++17, decomposition declarations even allow you to have the cool Python-like syntax to name the index and the value directly in the for
initializer:
从 C++17 开始,分解声明甚至允许你使用酷似 Python 的语法直接在for
初始化器中命名索引和值:
int main()
{
std::vector<double> vec = { 1., 2., 3., 4., 5. };
for (auto&& [index, value] : enumerate(vec)) {
value += index;
}
}
A C++-compliant compiler decomposes auto&&
inferring index
as std::size_t&&
and value
as double&
.
符合 C++ 的编译器分解auto&&
推断index
asstd::size_t&&
和value
as double&
。
回答by Karolis Juodel?
One way is to wrap the loop in a function of your own.
一种方法是将循环包装在您自己的函数中。
#include <iostream>
#include <vector>
#include <string>
template<typename T, typename F>
void mapWithIndex(std::vector<T> vec, F fun) {
for(int i = 0; i < vec.size(); i++)
fun(vec[i], i);
}
int main() {
std::vector<std::string> vec = {"hello", "cup", "of", "tea"};
mapWithIndex(vec, [](std::string s, int i){
std::cout << i << " " << s << '\n';
} );
}