【问题标题】:Python's enumerate for C++ [duplicate]C++的Python枚举[重复]
【发布时间】:2019-05-01 16:44:37
【问题描述】:

在 Python 中有enumerate,它接受一个序列/迭代器并产生一对整数索引和值本身。在 C++ 中,我偶尔会发现自己在写作

for (size_t i = 0; i != vector.size(); ++i) {
    auto const &elem = vector[i];
    // ...

类似于我想写的Python

for (auto const &it : enumerate(vector)) {
    // it.first is the index (size_t)
    // it.second is the element (T const&)

这样的enumerate 是否存在于 STL 或像 Boost 这样的通用库中?

【问题讨论】:

  • 老实说,我不确定你为什么会选择第二个 sn-p 而不是第一个。
  • @freakish 我可以看到。计数器变量有点新 :) 但是引入一个 3rd 方库似乎并不值得。
  • @freakish 你从来不想要那个?如果你有一个基于范围的 for 循环,只有在某些分支中需要索引怎么办?
  • @freakish:另一个不错的方面是可以使用std::transform 中的索引。

标签: c++ c++11


【解决方案1】:

这是一个使用高阶函数的版本。我喜欢它,因为它易于实现,并且不需要您了解 结构化绑定 的细微差别。它也不需要任何额外的依赖。

template <typename Container, typename F>
void enumerate(Container&& c, F&& f)
{
    std::size_t i = 0;
    for(auto&& x : std::forward<Container>(c)) 
    { 
         f(i++, forward_like<Container>(x)); 
    }
}

(如果Container 是一个右值,则forward_like 移动x。)

用法:

enumerate(std::vector{'a', 'b', 'c'}, [](auto index, auto x) 
{
    std::cout << index << ": " << x << '\n';
});

打印:

0: 'a'
1: 'b'
2: 'c'

live example on wandbox.org


C++11 兼容版本:live example on wandbox.org

【讨论】:

  • 通用 lambda 仅从 C++14 开始。不过,这还不错:)
  • 我的 Haskell 部分真的很喜欢这个!不幸的是,我目前正在处理的代码是一个高性能的生产代码,我们不得不感谢使用 C++11。我想保留这个答案,也许我们可以在五年内使用它。
  • @MartinUeding:你可以在 C++11 中使用它,只需将 lambda 中的 auto 替换为对象的具体类型即可。
  • @MartinUeding:在答案中添加了符合 C++11 的版本。
【解决方案2】:

这是一个使用range-v3 的示例。比手工制作的解决方案更冗长,但恕我直言,您可以从现有视图中组装出这样的范围,这很好。

#include <range/v3/view/indices.hpp>
#include <range/v3/view/zip.hpp>

using namespace ranges;

std::vector<int> vec{42, 43, 44};

for (const auto& idxAndValue : view::zip(view::indices, vec))
   std::cout << ideAndValue.first << " : " << idxAndValue.second << "\n";;

【讨论】:

  • 这是哪个 C++ 标准?它看起来不像 C++11,对吧?
  • @MartinUeding C++17,考虑将一个库包含在 C++20 的 std
  • 这个更新的 sn-p 编译,用clang++ -std=c++11 测试。
【解决方案3】:

另一种唯一有效的方法:

  1. 引用元素,并且
  2. 基于数组的容器,以及
  3. 元素不会重载operator&amp;


for(auto const &it : vector) {
    size_t index = &it - vector.data();
}

【讨论】:

  • 不幸的是有点冒险——忘了&amp;?容器没有正确的引用语义 (vector&lt;bool&gt;)?你的index 疯了,减法有 UB。但是,如果您知道自己在做什么,那么如果迭代本身不提供索引,这肯定是一种获取索引的方法。
  • @LightnessRacesinOrbit 添加了关于operator&amp;的注释。
  • 没想到。 std::addressof 可以成为你的朋友。但现在这变得越来越冗长了。
  • @LightnessRacesinOrbit 是的,但我想保持简单。
【解决方案4】:

是的,Boost's adapators::indexed 就是这样做的。

他们的示例(也使用现在冗余的 Boost.Assign 进行简洁的容器初始化)如下:

#include <boost/range/adaptor/indexed.hpp>
#include <boost/assign.hpp>
#include <iterator>
#include <iostream>
#include <vector>


int main(int argc, const char* argv[])
{
    using namespace boost::assign;
    using namespace boost::adaptors;

    std::vector<int> input;
    input += 10,20,30,40,50,60,70,80,90;

    for (const auto& element : input | indexed(0))
    {
        std::cout << "Element = " << element.value()
                  << " Index = " << element.index()
                  << std::endl;
    }

    return 0;
}

标准库中没有,尽管it's not hard to write

【讨论】:

  • FWIW,这是 boost enumerate.... 的最佳 Google 搜索结果。
  • 根据文档,您可以省略从零开始的索引。
  • @LightnessRacesinOrbit:我搜索了“C++ enumerate”,但没有考虑在搜索词中添加“boost”。感谢您的提示!
  • 在 C++11 中可以使用初始化列表,boost::assign 在这里不再需要。
  • @MaximEgorushkin 确实,因此是“现在冗余” - 但是我选择简单地逐字复制 Boost 示例。读者当然可以按照他们认为合适的方式进行改编(笑)。
猜你喜欢
  • 2016-03-05
  • 1970-01-01
  • 2012-07-04
  • 2014-08-18
  • 2023-04-01
  • 2023-03-26
  • 2011-03-15
  • 1970-01-01
  • 2011-04-24
相关资源
最近更新 更多