简体   繁体   English

在 C++11 foreach 循环中获取索引

[英]Get index in C++11 foreach loop

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 循环中获取当前容器条目的索引,例如在 python 中enumerate

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.当然我可以有一个计数器,但迭代器通常不保证它们在容器上迭代的顺序。

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;
    }
}

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.注意:范围循环和普通循环之间的顺序由标准 6.5.4 保证。 Meaning the counter is able to be coherent with the position in the container.这意味着计数器能够与容器中的位置保持一致。

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) 来源(这里还有其他例子)

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. C++17 和结构化绑定使这看起来不错 - 当然比一些带有局部[i = 0](Element&) mutable丑陋的可变 lambda 更好,或者我在承认可能不是所有东西都应该硬塞进for_each()之前所做的任何事情等。 - and than other solutions that require a counter with scope outside the for loop. - 而不是其他需要在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 P2164 proposes 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 a struct with 2 members index and value representing respectively the position and value of the elements in the adapted range.我们提出了一个视图enumerate它的值类型是一个struct有 2 个成员indexvalue分别表示适应范围内元素的位置和值。

[ . [ . . . .] .]

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 ).这个特性以某种形式存在于 Python、Rust、Go(支持语言)和许多 C++ 库中: ranges-v3follyboost::rangesindexed )。

The existence of this feature or lack thereof is the subject of recurring stackoverflow questions.此功能是否存在是反复出现的 stackoverflow 问题的主题。

Hey, look!你看! We're famous.我们很有名。

If you need the index then a traditional for works perfectly well.如果您需要索引,那么传统的 for 工作得很好。

for (int idx=0; idx<num; ++idx)
{
// do stuff
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM