可能重复:
Find position of element in C++11 range-based for loop?
我有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());
我想知道是否有更紧凑的解决方案。与Python的enumerate类似的东西。这是我使用C ++ 11范围循环时最接近的,但是必须在私有范围内定义循环外的索引,这似乎比1或2更糟糕的解决方案:
{
int idx = 0;
for (auto& elem : v)
foo(elem, idx++);
}
有没有办法(可能使用Boost)以索引自包含到循环中的方式简化最新的示例?
答案 0 :(得分:17)
这是一种使用懒惰评估的有趣解决方案。首先,构造生成器对象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 };
}
};
然后,创建一个包装函数枚举,它将推导出模板参数并返回生成器:
template<typename Iterable>
auto enumerate(Iterable&& iter)
-> enumerate_object<Iterable>
{
return { std::forward<Iterable>(iter) };
}
您现在可以使用您的功能:
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;
}
}
上面的实现只是一个玩具:它应该同时使用const
和非const
左值引用以及右值引用,但考虑到后者的实际成本,它会多次复制整个可迭代对象。这个问题肯定可以通过额外的调整来解决。
从C ++ 17开始,分解声明甚至允许你使用类似Python的类似语法来直接在for
初始值设定项中命名索引和值:
int main()
{
std::vector<double> vec = { 1., 2., 3., 4., 5. };
for (auto&& [index, value] a: enumerate(vec)) {
value += index;
}
}
我手头没有符合C ++ 17的编译器来检查它,但我希望分解中的auto&&
能够将index
推断为std::size_t
和value
double&
。
答案 1 :(得分:13)
正如@Kos所说,这是一件非常简单的事情,我并没有真正看到需要进一步简化它,并且只会坚持使用索引的传统for循环,除了我放弃{{1}并简单地使用std::vector<T>::size_type
:
std::size_t
我不太热衷于解决方案2.它需要(有点隐藏)随机访问迭代器,它不允许您轻松交换容器,这是迭代器的优点之一。如果你想使用迭代器并使其成为通用的(并且当迭代器不随机访问时可能会导致性能损失),我建议使用for(std::size_t i = 0; i < v.size(); ++i)
foo(v[i], i);
:
std::distance
答案 2 :(得分:1)
一种方法是将循环包装在您自己的函数中。
#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';
} );
}