在range-for循环中访问索引

时间:2012-09-12 23:42:39

标签: c++ c++11

我有一个对象向量,并使用range-for循环迭代它。我用它来打印对象中的函数,如下所示:

vector<thisObject> storedValues;
//put stuff in storedValues
for(auto i:storedValues)
{
   cout<<i.function();
}

但我也要打印索引。我想要的输出是:

1: value
2: value
//etc

我打算只使用我每次增加的计数器,但这看起来非常低效。还有更好的方法吗?

6 个答案:

答案 0 :(得分:29)

你做不到。 索引是向量的特定概念,而不是集合的通用属性。另一方面,基于范围的循环是迭代任何集合的每个元素的通用机制。

如果您确实想要使用特定容器实现的详细信息,只需使用普通循环:

for (std::size_t i = 0, e = v.size(); i != e; ++i) { /* ... */ }

重复这一点:基于范围的循环用于操作任何集合的每个元素,其中集合本身无关紧要,并且在循环体内从不提及容器。它只是您工具箱中的另一个工具,并且您不必将其用于绝对一切。相反,如果您想要改变集合(例如删除或随机元素),或者使用有关集合结构的特定信息,请使用普通循环。

答案 1 :(得分:11)

我确信有些人不会喜欢这个,但我已经创建了一个预处理器宏,以(IMO)相对干净的方式为你处理这个问题:

#define for_indexed(...) for_indexed_v(i, __VA_ARGS__)
#define for_indexed_v(v, ...) for(bool _i_ = true, _break_ = false; _i_;) for(size_t v = 0; _i_; _i_ = false) for(__VA_ARGS__) if(_break_) break; else for(bool _j_ = true; _j_;) for(_break_ = true; _j_; _j_ = false) for(bool _k_ = true; _k_; v++, _k_ = false, _break_ = false)

使用示例:

std::vector<int> v {1, 2, 3};
for_indexed (const auto& item : v) {
    if (i > 0) std::cout << ", ";
    std::cout << i << ": " << item;
}

使用不同的循环变量:

for_indexed_v (my_cool_counter, const auto& item : v) ...

我已经仔细检查了,这个额外的循环内容都在-O1或更高版本上进行了优化。你留下了一个很好的,易于阅读的循环语法。

奖励:这也适用于经典的iterator - 样式循环。 (虽然您也可以使用std::distance(begin, it)。)

更新28/05/2017 :使break;语句正常工作
更新28/01/2019 :将for放在宏名称中,以便单词indexed是有效的变量名称。我怀疑for_indexed会导致任何冲突。

答案 2 :(得分:7)

使用range-v3Range-v3是由ISO C ++委员会成员Eric Niebler设计和实现的下一代范围库,预计将在未来合并为C ++标准。

使用range-v3 OP的问题可以轻松解决:

using ranges::v3::view::zip;
using ranges::v3::view::ints;

for(auto &&[i, idx]: zip(storedValues, ints(0u))){
    std::cout << idx << ": " << i.function() << '\n';
}

您需要一个支持C ++ 17或更高版本的编译器来编译这段代码,不仅用于structured binding语法,还用于begin的返回类型和返回值end的{​​{1}}函数不同。

您可以看到在线示例hereranges::v3::view::zip的文档为here,源代码本身已托管here。如果您使用的是MSVC编译器,也可以查看here

答案 3 :(得分:5)

您可以使用range-v3enumerate视图:

std::vector<thisObject> storedValues;
for (auto const& [idx, value] : storedValues | ranges::view::enumerate) {
  std::cout << idx << ": " << value << '\n';
}

在C ++ 20中,additional initializations将在range-for循环中引入:

std::vector<thisObject> storedValues;
for (size_t idx = 0; auto value : storedValues) {
  std::cout << idx << ": " << value << '\n';
  ++idx;
}

答案 4 :(得分:1)

说实话,这很简单,只是要弄清楚您可以减去地址:)

&i 将引用内存中的地址,并且从索引到索引将增加4,因为它拥有定义的向量类型的整数。现在&values [0] 引用第一个点,当您减去2个地址时,两者之差分别为0、4、8、12,但实际上,它减去了整数的大小类型,通常为4个字节。 所以对应地0 = 0 int,4 = 1 int,8 = 2nd int,12 = 3rd int

这里是矢量

vector<int> values = {10,30,9,8};

for(auto &i: values) {

cout << "index: " <<  &i  - &values[0]; 
cout << "\tvalue: " << i << endl;

}

这里是用于常规数组的,几乎是同一件事

int values[]= {10,30,9,8};

for(auto &i: values) {

cout << "index: " <<  &i  - &values[0];
cout << "\tvalue: " << i << endl;

}

注意,这是针对C ++ 11的,如果您使用的是g ++,请记住使用 -std = c ++ 11 参数进行编译

答案 5 :(得分:0)

这是c ++ 17的预处理程序宏版本的更新版本。与手动编写带有索引的循环相比,Clang生成相同的调试和优化代码。 MSVC会生成相同的优化代码,但会在调试中添加一些额外的指令。

#define for_index(...) for_index_v(i, __VA_ARGS__)
#define for_index_v(i, ...) if (size_t i##_next = 0; true) for (__VA_ARGS__) if (size_t i = i##_next++; true)

我试图挖掘MSVC在调试版本中添加的额外代码,但我不确定它的用途是什么。它将在循环开始时添加以下内容:

        xor     eax, eax
        cmp     eax, 1
        je      $LN5@for_i

这将完全跳过循环。使用的示例:https://godbolt.org/z/VTWhgT