在 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 00:50:18  来源:igfitidea点击:

Get index in C++11 foreach loop

c++c++11foreach

提问by hildensia

Is there a convenient way to get the index of the current container entry in a C++11 foreach loop, like enumeratein 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 forloop 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&) mutableor 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 forloop.

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 enumeratewhose value type is a structwith 2 members indexand valuerepresenting 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 个成员indexvalue分别表示适应范围内元素的位置和值。

[ . . .]

此功能以某种形式存在于 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
}