如何在基于范围的for循环中找到当前对象的索引?

发布于 2025-01-09 12:41:06 字数 165 浏览 0 评论 0原文

假设我有以下代码:

vector<int> list;
for(auto& elem:list) {
    int i = elem;
}

我可以在向量中找到 elem 的位置而不维护单独的迭代器吗?

Assume I have the following code:

vector<int> list;
for(auto& elem:list) {
    int i = elem;
}

Can I find the position of elem in the vector without maintaining a separate iterator?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

梦晓ヶ微光ヅ倾城 2025-01-16 12:41:06

是的,你可以,只需要一些按摩;)

技巧是使用组合:不是直接迭代容器,而是沿途使用索引“压缩”它。

专门的拉链代码:

template <typename T>
struct iterator_extractor { typedef typename T::iterator type; };

template <typename T>
struct iterator_extractor<T const> { typedef typename T::const_iterator type; };


template <typename T>
class Indexer {
public:
    class iterator {
        typedef typename iterator_extractor<T>::type inner_iterator;

        typedef typename std::iterator_traits<inner_iterator>::reference inner_reference;
    public:
        typedef std::pair<size_t, inner_reference> reference;

        iterator(inner_iterator it): _pos(0), _it(it) {}

        reference operator*() const { return reference(_pos, *_it); }

        iterator& operator++() { ++_pos; ++_it; return *this; }
        iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; }

        bool operator==(iterator const& it) const { return _it == it._it; }
        bool operator!=(iterator const& it) const { return !(*this == it); }

    private:
        size_t _pos;
        inner_iterator _it;
    };

    Indexer(T& t): _container(t) {}

    iterator begin() const { return iterator(_container.begin()); }
    iterator end() const { return iterator(_container.end()); }

private:
    T& _container;
}; // class Indexer

template <typename T>
Indexer<T> index(T& t) { return Indexer<T>(t); }

并使用它:

#include <iostream>
#include <iterator>
#include <limits>
#include <vector>

// Zipper code here

int main() {
    std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9};

    for (auto p: index(v)) {
        std::cout << p.first << ": " << p.second << "\n";
    }
}

您可以在 ideone 看到它,尽管它缺乏 for-range 循环支持,因此它不太漂亮。

编辑:

只是记得我应该更频繁地检查 Boost.Range。不幸的是没有zip范围,但我确实找到了一颗珍珠:boost::adaptors::indexed。然而,它需要访问迭代器来提取索引。羞耻:x

否则使用 counting_range 和通用的 zip 我确信可以做一些有趣的事情......

在我想象的理想世界中:

int main() {
    std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9};

    for (auto tuple: zip(iota(0), v)) {
        std::cout << tuple.at<0>() << ": " << tuple.at<1>() << "\n";
    }
}

使用 zip 自动创建视图作为引用元组的范围,并且 iota(0) 只是创建一个从 0 开始的“假”范围,并且只向无穷大计数(或者,最大其类型...)。

Yes you can, it just take some massaging ;)

The trick is to use composition: instead of iterating over the container directly, you "zip" it with an index along the way.

Specialized zipper code:

template <typename T>
struct iterator_extractor { typedef typename T::iterator type; };

template <typename T>
struct iterator_extractor<T const> { typedef typename T::const_iterator type; };


template <typename T>
class Indexer {
public:
    class iterator {
        typedef typename iterator_extractor<T>::type inner_iterator;

        typedef typename std::iterator_traits<inner_iterator>::reference inner_reference;
    public:
        typedef std::pair<size_t, inner_reference> reference;

        iterator(inner_iterator it): _pos(0), _it(it) {}

        reference operator*() const { return reference(_pos, *_it); }

        iterator& operator++() { ++_pos; ++_it; return *this; }
        iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; }

        bool operator==(iterator const& it) const { return _it == it._it; }
        bool operator!=(iterator const& it) const { return !(*this == it); }

    private:
        size_t _pos;
        inner_iterator _it;
    };

    Indexer(T& t): _container(t) {}

    iterator begin() const { return iterator(_container.begin()); }
    iterator end() const { return iterator(_container.end()); }

private:
    T& _container;
}; // class Indexer

template <typename T>
Indexer<T> index(T& t) { return Indexer<T>(t); }

And using it:

#include <iostream>
#include <iterator>
#include <limits>
#include <vector>

// Zipper code here

int main() {
    std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9};

    for (auto p: index(v)) {
        std::cout << p.first << ": " << p.second << "\n";
    }
}

You can see it at ideone, though it lacks the for-range loop support so it's less pretty.

EDIT:

Just remembered that I should check Boost.Range more often. Unfortunately no zip range, but I did found a pearl: boost::adaptors::indexed. However it requires access to the iterator to pull of the index. Shame :x

Otherwise with the counting_range and a generic zip I am sure it could be possible to do something interesting...

In the ideal world I would imagine:

int main() {
    std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9};

    for (auto tuple: zip(iota(0), v)) {
        std::cout << tuple.at<0>() << ": " << tuple.at<1>() << "\n";
    }
}

With zip automatically creating a view as a range of tuples of references and iota(0) simply creating a "false" range that starts from 0 and just counts toward infinity (or well, the maximum of its type...).

彼岸花似海 2025-01-16 12:41:06

jrok 是对的:基于范围的 for 循环不是为此目的而设计的。

但是,在您的情况下,可以使用指针算术来计算它,因为 vector 连续存储其元素 (*)

vector<int> list;
for(auto& elem:list) { 
    int i = elem;
    int pos = &elem-&list[0]; // pos contains the position in the vector 

    // also a &-operator overload proof alternative (thanks to ildjarn) :
    // int pos = addressof(elem)-addressof(list[0]); 

}

但这显然是一种不好的做法,因为它混淆了代码和代码。使其更加脆弱(如果有人更改容器类型、重载 & 运算符或将 'auto&' 替换为 'auto',它很容易损坏。祝调试顺利!)

注意:保证连续性对于 C++03 中的向量,以及 C++11 标准中的数组和字符串。

jrok is right : range-based for loops are not designed for that purpose.

However, in your case it is possible to compute it using pointer arithmetic since vector stores its elements contiguously (*)

vector<int> list;
for(auto& elem:list) { 
    int i = elem;
    int pos = &elem-&list[0]; // pos contains the position in the vector 

    // also a &-operator overload proof alternative (thanks to ildjarn) :
    // int pos = addressof(elem)-addressof(list[0]); 

}

But this is clearly a bad practice since it obfuscates the code & makes it more fragile (it easily breaks if someone changes the container type, overload the & operator or replace 'auto&' by 'auto'. good luck to debug that!)

NOTE: Contiguity is guaranteed for vector in C++03, and array and string in C++11 standard.

悍妇囚夫 2025-01-16 12:41:06

不,你不能(至少,不努力的话)。如果您需要元素的位置,则不应使用基于范围的 for。请记住,它只是适用于最常见情况的便利工具:为每个元素执行一些代码。在不太常见的情况下,您需要元素的位置,您必须使用不太方便的常规 for 循环。

No, you can't (at least, not without effort). If you need the position of an element, you shouldn't use range-based for. Remember that it's just a convenience tool for the most common case: execute some code for each element. In the less-common circumstances where you need the position of the element, you have to use the less-convenient regular for loop.

说好的呢 2025-01-16 12:41:06

根据@Matthieu的答案,有一个非常优雅的解决方案,使用提到的 boost::adaptors::indexed:

std::vector<std::string> strings{10, "Hello"};
int main(){
    strings[5] = "World";
    for(auto const& el: strings| boost::adaptors::indexed(0))
      std::cout << el.index() << ": " << el.value() << std::endl;
}

您可以尝试一下

这与提到的“理想世界解决方案”非常相似,具有漂亮的语法并且简洁。请注意,在这种情况下,el 的类型类似于 boost::foobar,因此它处理那里的引用,而不进行复制被执行。它的效率甚至令人难以置信: https://godbolt.org/g/e4LMnJ (代码相当于保留一个自己的计数器变量,它尽可能好)

为了完整性,替代方案:

size_t i = 0;
for(auto const& el: strings) {
  std::cout << i << ": " << el << std::endl;
  ++i;
}

或使用向量的连续属性:

for(auto const& el: strings) {
  size_t i = &el - &strings.front();
  std::cout << i << ": " << el << std::endl;
}

第一个生成与升压适配器版本相同的代码(最佳),最后一个长1条指令: https://godbolt.org/g/nEG8f9

注意:如果您只想知道,如果您有您可以使用的最后一个元素:

for(auto const& el: strings) {
  bool isLast = &el == &strings.back();
  std::cout << isLast << ": " << el << std::endl;
}

这适用于每个标准容器,但必须使用 auto&/auto const& (与上面相同),但无论如何建议这样做。根据输入,这可能也相当快(特别是当编译器知道向量的大小时)

&foo 替换为 std::addressof(foo) 到确保通用代码的安全。

Based on the answer from @Matthieu there is a very elegant solution using the mentioned boost::adaptors::indexed:

std::vector<std::string> strings{10, "Hello"};
int main(){
    strings[5] = "World";
    for(auto const& el: strings| boost::adaptors::indexed(0))
      std::cout << el.index() << ": " << el.value() << std::endl;
}

You can try it

This works pretty much like the "ideal world solution" mentioned, has pretty syntax and is concise. Note that the type of el in this case is something like boost::foobar<const std::string&, int>, so it handles the reference there and no copying is performed. It is even incredibly efficient: https://godbolt.org/g/e4LMnJ (The code is equivalent to keeping an own counter variable which is as good as it gets)

For completeness the alternatives:

size_t i = 0;
for(auto const& el: strings) {
  std::cout << i << ": " << el << std::endl;
  ++i;
}

Or using the contiguous property of a vector:

for(auto const& el: strings) {
  size_t i = &el - &strings.front();
  std::cout << i << ": " << el << std::endl;
}

The first generates the same code as the boost adapter version (optimal) and the last is 1 instruction longer: https://godbolt.org/g/nEG8f9

Note: If you only want to know, if you have the last element you can use:

for(auto const& el: strings) {
  bool isLast = &el == &strings.back();
  std::cout << isLast << ": " << el << std::endl;
}

This works for every standard container but auto&/auto const& must be used (same as above) but that is recommended anyway. Depending on the input this might also be pretty fast (especially when the compiler knows the size of your vector)

Replace the &foo by std::addressof(foo) to be on the safe side for generic code.

稚然 2025-01-16 12:41:06

如果您有支持 C++14 的编译器,则可以以函数式方式执行此操作:

#include <iostream>
#include <string>
#include <vector>
#include <functional>

template<typename T>
void for_enum(T& container, std::function<void(int, typename T::value_type&)> op)
{
    int idx = 0;
    for(auto& value : container)
        op(idx++, value);
}

int main()
{
    std::vector<std::string> sv {"hi", "there"};
    for_enum(sv, [](auto i, auto v) {
        std::cout << i << " " << v << std::endl;
    });
}

适用于 clang 3.4 和 gcc 4.9(不适用于 4.8);两者都需要设置-std=c++1y。您需要 c++14 的原因是 lambda 函数中的 auto 参数。

If you have a compiler with C++14 support you can do it in a functional style:

#include <iostream>
#include <string>
#include <vector>
#include <functional>

template<typename T>
void for_enum(T& container, std::function<void(int, typename T::value_type&)> op)
{
    int idx = 0;
    for(auto& value : container)
        op(idx++, value);
}

int main()
{
    std::vector<std::string> sv {"hi", "there"};
    for_enum(sv, [](auto i, auto v) {
        std::cout << i << " " << v << std::endl;
    });
}

Works with clang 3.4 and gcc 4.9 (not with 4.8); for both need to set -std=c++1y. The reason you need c++14 is because of the auto parameters in the lambda function.

挽手叙旧 2025-01-16 12:41:06

有一种非常简单的方法可以做到这一点

vector<int> list;
for(auto& elem:list) {
    int i = (&elem-&*(list.begin()));
}

,其中 i 将是您所需的索引。

这利用了 C++ 向量始终是连续的这一事实。

There is a surprisingly simple way to do this

vector<int> list;
for(auto& elem:list) {
    int i = (&elem-&*(list.begin()));
}

where i will be your required index.

This takes advantage of the fact that C++ vectors are always contiguous.

李白 2025-01-16 12:41:06

如果您坚持使用基于范围并了解索引,则维护索引非常简单,如下所示。
我认为对于基于范围的 for 循环没有更干净/更简单的解决方案。但为什么不使用 for(;;) 的标准呢?这可能会让您的意图和代码最清晰。

vector<int> list;
int idx = 0;
for(auto& elem:list) {
    int i = elem;
    //TODO whatever made you want the idx
    ++idx;
}

If you insist on using range based for, and to know index, it is pretty trivial to maintain index as shown below.
I do not think there is a cleaner / simpler solution for range based for loops. But really why not use a standard for(;;)? That probably would make your intent and code the clearest.

vector<int> list;
int idx = 0;
for(auto& elem:list) {
    int i = elem;
    //TODO whatever made you want the idx
    ++idx;
}
话少情深 2025-01-16 12:41:06

这是一个使用 c++20 的非常漂亮的解决方案:

#include <array>
#include <iostream>
#include <ranges>

template<typename T>
struct EnumeratedElement {
    std::size_t index;
    T& element;
};

auto enumerate(std::ranges::range auto& range) 
    -> std::ranges::view auto 
{
    return range | std::views::transform(
        [i = std::size_t{}](auto& element) mutable {
            return EnumeratedElement{i++, element};
        }
    );
}

auto main() -> int {
    auto const elements = std::array{3, 1, 4, 1, 5, 9, 2};
    for (auto const [index, element] : enumerate(elements)) {
        std::cout << "Element " << index << ": " << element << '\n';
    }
}

这里使用的主要功能是 c++20 范围、c++20 概念、c++11 可变 lambda、c++14 lambda 捕获初始值设定项和 c++17 结构化绑定。有关这些主题的信息,请参阅 cppreference.com。

请注意,结构化绑定中的 element 实际上是一个引用,而不是元素的副本(这里并不重要)。这是因为 auto 周围的任何限定符仅影响从中提取字段的临时对象,而不影响字段本身。

生成的代码与由此生成的代码相同(至少由 gcc 10.2 生成):

#include <array>
#include <iostream>
#include <ranges>

auto main() -> int {
    auto const elements = std::array{3, 1, 4, 1, 5, 9, 2};
    for (auto index = std::size_t{}; auto& element : elements) {
        std::cout << "Element " << index << ": " << element << '\n';
        index++;
    }
}

证明: https://godbolt. org/z/a5bfxz

Here's a quite beautiful solution using c++20:

#include <array>
#include <iostream>
#include <ranges>

template<typename T>
struct EnumeratedElement {
    std::size_t index;
    T& element;
};

auto enumerate(std::ranges::range auto& range) 
    -> std::ranges::view auto 
{
    return range | std::views::transform(
        [i = std::size_t{}](auto& element) mutable {
            return EnumeratedElement{i++, element};
        }
    );
}

auto main() -> int {
    auto const elements = std::array{3, 1, 4, 1, 5, 9, 2};
    for (auto const [index, element] : enumerate(elements)) {
        std::cout << "Element " << index << ": " << element << '\n';
    }
}

The major features used here are c++20 ranges, c++20 concepts, c++11 mutable lambdas, c++14 lambda capture initializers, and c++17 structured bindings. Refer to cppreference.com for information on any of these topics.

Note that element in the structured binding is in fact a reference and not a copy of the element (not that it matters here). This is because any qualifiers around the auto only affect a temporary object that the fields are extracted from, and not the fields themselves.

The generated code is identical to the code generated by this (at least by gcc 10.2):

#include <array>
#include <iostream>
#include <ranges>

auto main() -> int {
    auto const elements = std::array{3, 1, 4, 1, 5, 9, 2};
    for (auto index = std::size_t{}; auto& element : elements) {
        std::cout << "Element " << index << ": " << element << '\n';
        index++;
    }
}

Proof: https://godbolt.org/z/a5bfxz

手长情犹 2025-01-16 12:41:06

您可以使用 std::views::enumerate (C++23) 来获取元素的索引。

#include <iostream>
#include <ranges>
#include <vector>
 
int main()
{
    const auto letters = std::vector{'A', 'B', 'C', 'D', 'E'};
 
    for (const auto& [index, letter] : std::views::enumerate(letters))
    {
        std::cout << '(' << index << ':' << letter << ") ";
    }
}

检查 C++ 23 编译器支持页面,了解您的编译器是否支持此功能。

You can use std::views::enumerate (C++23) to get the index of elements.

#include <iostream>
#include <ranges>
#include <vector>
 
int main()
{
    const auto letters = std::vector{'A', 'B', 'C', 'D', 'E'};
 
    for (const auto& [index, letter] : std::views::enumerate(letters))
    {
        std::cout << '(' << index << ':' << letter << ") ";
    }
}

Check the C++ 23 compiler support page to see if your compiler supports this feature.

夏末的微笑 2025-01-16 12:41:06

我从您的评论中读到,您想知道索引的一个原因是了解该元素是否是序列中的第一个/最后一个。如果是这样,您可以执行

for(auto& elem:list) {
//  loop code ...
    if(&elem == &*std::begin(list)){ ... special code for first element ... }
    if(&elem == &*std::prev(std::end(list))){ ... special code for last element ... }
//  if(&elem == &*std::rbegin(list)){... (C++14 only) special code for last element ...}
//  loop code ... 
}

编辑: 例如,这会打印一个容器,跳过最后一个元素中的分隔符。适用于我能想象到的大多数容器(包括数组),(在线演示 http://coliru.stacked- crooked.com/a/9bdce059abd87f91):

#include <iostream>
#include <vector>
#include <list>
#include <set>
using namespace std;

template<class Container>
void print(Container const& c){
  for(auto& x:c){
    std::cout << x; 
    if(&x != &*std::prev(std::end(c))) std::cout << ", "; // special code for last element
  }
  std::cout << std::endl;
}

int main() {
  std::vector<double> v{1.,2.,3.};
  print(v); // prints 1,2,3
  std::list<double> l{1.,2.,3.};
  print(l); // prints 1,2,3
  std::initializer_list<double> i{1.,2.,3.};
  print(i); // prints 1,2,3
  std::set<double> s{1.,2.,3.};
  print(s); // print 1,2,3
  double a[3] = {1.,2.,3.}; // works for C-arrays as well
  print(a); // print 1,2,3
}

I read from your comments that one reason you want to know the index is to know if the element is the first/last in the sequence. If so, you can do

for(auto& elem:list) {
//  loop code ...
    if(&elem == &*std::begin(list)){ ... special code for first element ... }
    if(&elem == &*std::prev(std::end(list))){ ... special code for last element ... }
//  if(&elem == &*std::rbegin(list)){... (C++14 only) special code for last element ...}
//  loop code ... 
}

EDIT: For example, this prints a container skipping a separator in the last element. Works for most containers I can imagine (including arrays), (online demo http://coliru.stacked-crooked.com/a/9bdce059abd87f91):

#include <iostream>
#include <vector>
#include <list>
#include <set>
using namespace std;

template<class Container>
void print(Container const& c){
  for(auto& x:c){
    std::cout << x; 
    if(&x != &*std::prev(std::end(c))) std::cout << ", "; // special code for last element
  }
  std::cout << std::endl;
}

int main() {
  std::vector<double> v{1.,2.,3.};
  print(v); // prints 1,2,3
  std::list<double> l{1.,2.,3.};
  print(l); // prints 1,2,3
  std::initializer_list<double> i{1.,2.,3.};
  print(i); // prints 1,2,3
  std::set<double> s{1.,2.,3.};
  print(s); // print 1,2,3
  double a[3] = {1.,2.,3.}; // works for C-arrays as well
  print(a); // print 1,2,3
}
生来就爱笑 2025-01-16 12:41:06

Tobias Widlund 编写了一个很好的 MIT 许可的 Python 样式标头,仅枚举(尽管是 C++17):

博客文章

真的很好用:

std::vector<int> my_vector {1,3,3,7};

for(auto [i, my_element] : en::enumerate(my_vector))
{
    // do stuff
}

Tobias Widlund wrote a nice MIT licensed Python style header only enumerate (C++17 though):

GitHub

Blog Post

Really nice to use:

std::vector<int> my_vector {1,3,3,7};

for(auto [i, my_element] : en::enumerate(my_vector))
{
    // do stuff
}
七七 2025-01-16 12:41:06

这是一个基于宏的解决方案,它可能在简单性、编译时间和代码生成质量方面胜过大多数其他解决方案:

#include <iostream>

#define fori(i, ...) if(size_t i = -1) for(__VA_ARGS__) if(i++, true)

int main() {
    fori(i, auto const & x : {"hello", "world", "!"}) {
        std::cout << i << " " << x << std::endl;
    }
}

结果:

$ g++ -o enumerate enumerate.cpp -std=c++11 && ./enumerate 
0 hello
1 world
2 !

Here's a macro-based solution that probably beats most others on simplicity, compile time, and code generation quality:

#include <iostream>

#define fori(i, ...) if(size_t i = -1) for(__VA_ARGS__) if(i++, true)

int main() {
    fori(i, auto const & x : {"hello", "world", "!"}) {
        std::cout << i << " " << x << std::endl;
    }
}

Result:

$ g++ -o enumerate enumerate.cpp -std=c++11 && ./enumerate 
0 hello
1 world
2 !
热风软妹 2025-01-16 12:41:06

如果您想避免编写辅助函数
循环本地的索引变量,您可以将 lambda 与可变变量一起使用。:

int main() {
    std::vector<char> values = {'a', 'b', 'c'};
    std::for_each(begin(values), end(values), [i = size_t{}] (auto x) mutable {
        std::cout << i << ' ' << x << '\n';
        ++i;
    });
}

If you want to avoid having to write an auxiliary function while having
the index variable local to the loop, you can use a lambda with a mutable variable.:

int main() {
    std::vector<char> values = {'a', 'b', 'c'};
    std::for_each(begin(values), end(values), [i = size_t{}] (auto x) mutable {
        std::cout << i << ' ' << x << '\n';
        ++i;
    });
}
夏末染殇 2025-01-16 12:41:06

以下代码支持对命名和未命名(临时)对象的迭代:

#include <utility>

template<typename Iterable>
class enumerate_named_object
{
private:
    static Iterable container();
    std::size_t size_;
    decltype(std::begin(container())) begin_;
    decltype(std::end(container())) end_;

public:
    enumerate_named_object(Iterable& iter)
        : size_(0)
        , begin_(std::begin(iter))
        , end_(std::end(iter))
    {}

    const enumerate_named_object& begin() const { return *this; }
    const enumerate_named_object& end()   const { return *this; }

    bool operator != (const enumerate_named_object&) const
    {
        return begin_ != end_;
    }

    void operator++()
    {
        ++begin_;
        ++size_;
    }

    std::pair<std::size_t, decltype(*begin_)> operator*() const {
        return { size_, *begin_ };
    }
};

template<typename Iterable>
class enumerate_temporary_object
{
private:
    Iterable container_;
    std::size_t size_;
    decltype(std::begin(container_)) begin_;
    decltype(std::end(container_)) end_;

public:
    struct end_type {};
    enumerate_temporary_object(enumerate_temporary_object&& other) noexcept
        : enumerate_temporary_object{ std::move(other.container_) }
    {}
    enumerate_temporary_object(Iterable&& iter)
        : container_(std::move(iter))
        , size_(0)
        , begin_(std::begin(container_))
        , end_(std::end(container_))
    {}

    enumerate_temporary_object begin() { return std::move(*this); }
    end_type end() const { return {}; }

    bool operator != (const end_type&) const
    {
        return begin_ != end_;
    }

    void operator++()
    {
        ++begin_;
        ++size_;
    }

    std::pair<std::size_t, decltype(*begin_)> operator*() const {
        return { size_, *begin_ };
    }
};

template<typename Iterable>
auto enumerate(Iterable&& iter) -> enumerate_temporary_object<Iterable>
{
    return { std::move(iter) };
}

template<typename Iterable>
auto enumerate(Iterable& iter) -> enumerate_named_object<Iterable&>
{
    return { iter };
}

template<typename Iterable>
auto enumerate(const Iterable& iter) -> enumerate_named_object<const Iterable&>
{
    return { iter };
}

The following code supports iteration over named as well as unnamed (temporary) objects:

#include <utility>

template<typename Iterable>
class enumerate_named_object
{
private:
    static Iterable container();
    std::size_t size_;
    decltype(std::begin(container())) begin_;
    decltype(std::end(container())) end_;

public:
    enumerate_named_object(Iterable& iter)
        : size_(0)
        , begin_(std::begin(iter))
        , end_(std::end(iter))
    {}

    const enumerate_named_object& begin() const { return *this; }
    const enumerate_named_object& end()   const { return *this; }

    bool operator != (const enumerate_named_object&) const
    {
        return begin_ != end_;
    }

    void operator++()
    {
        ++begin_;
        ++size_;
    }

    std::pair<std::size_t, decltype(*begin_)> operator*() const {
        return { size_, *begin_ };
    }
};

template<typename Iterable>
class enumerate_temporary_object
{
private:
    Iterable container_;
    std::size_t size_;
    decltype(std::begin(container_)) begin_;
    decltype(std::end(container_)) end_;

public:
    struct end_type {};
    enumerate_temporary_object(enumerate_temporary_object&& other) noexcept
        : enumerate_temporary_object{ std::move(other.container_) }
    {}
    enumerate_temporary_object(Iterable&& iter)
        : container_(std::move(iter))
        , size_(0)
        , begin_(std::begin(container_))
        , end_(std::end(container_))
    {}

    enumerate_temporary_object begin() { return std::move(*this); }
    end_type end() const { return {}; }

    bool operator != (const end_type&) const
    {
        return begin_ != end_;
    }

    void operator++()
    {
        ++begin_;
        ++size_;
    }

    std::pair<std::size_t, decltype(*begin_)> operator*() const {
        return { size_, *begin_ };
    }
};

template<typename Iterable>
auto enumerate(Iterable&& iter) -> enumerate_temporary_object<Iterable>
{
    return { std::move(iter) };
}

template<typename Iterable>
auto enumerate(Iterable& iter) -> enumerate_named_object<Iterable&>
{
    return { iter };
}

template<typename Iterable>
auto enumerate(const Iterable& iter) -> enumerate_named_object<const Iterable&>
{
    return { iter };
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文