在range-for循环中访问索引

24 c++ c++11

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

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

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

1: value
2: value
//etc
Run Code Online (Sandbox Code Playgroud)

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

Ker*_* SB 31

你不能.该指数是一个特定的概念,以一个矢量,而不是一个集合的一般属性.另一方面,基于范围的循环是迭代任何集合的每个元素的通用机制.

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

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

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

  • 感谢您的答复.我对基于范围的循环有点过分兴奋. (3认同)
  • 你通常会这样保存结局吗?循环不变优化应该解决在现代编译器中多次调用 size() 时遇到的任何问题。 (2认同)
  • 基于范围的 for 循环与集合无关。他们操纵_范围内_的每个元素。也就是说,如果定义范围的迭代器至少为 _ForwardIterator_-s,则范围开头与实际位置之间的_距离_也是明确定义的。 (2认同)

Tha*_*esy 12

我敢肯定有些人不会喜欢这个,但我已经创建了一个预处理器宏,以(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)
Run Code Online (Sandbox Code Playgroud)

用法示例:

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

要使用不同的循环变量:

for_indexed_v (my_cool_counter, const auto& item : v) ...
Run Code Online (Sandbox Code Playgroud)

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

额外奖励:这也适用于经典iterator风格的循环.(虽然你也可以使用std::distance(begin, it).)

更新日期:28/05/2017:Made break;语句正常工作
更新28/01/2019:for输入宏名称,以便该单词indexed是有效的变量名称.我怀疑for_indexed会引起任何冲突.


cbu*_*art 9

您可以使用range-v3enumerate视图:

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

C++20 将在 range-for 循环中引入额外的初始化

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

  • 范围循环中的 C++20 附加初始化是一个有价值的补充 (7认同)

gna*_*yil 8

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

通过使用range-v3OP的问题可以轻松解决:

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

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

您将需要一个支持C ++ 17或更高版本的编译器来编译这段代码,这不仅用于结构化绑定语法,而且还用于以下事实:返回值的函数类型beginend返回值的函数ranges::v3::view::zip不同。

您可以在此处看到在线示例。的文档range-v3位于此处,源代码本身位于此处。如果您使用的是MSVC编译器,也可以在这里查看

  • 有趣的是,这个特性在 PHP 中存在多年,如 `foreach($data as $key =&gt; $value) { /* ... */ }` 或 `foreach($data as $value) { /* ... */ }` 如果你不想要索引 itaree。但是你知道每个人都对这种语言撒尿...... (2认同)
  • @gnaggnoyil RomainLaneuville 所说的是,在 foreach 循环(范围循环)中获取数组的索引已经在 PHP 中存在多年了。它是语言的一部分。不需要额外的库或更新版本 (2认同)

小智 5

老实说,这很简单,只需弄清楚你可以减去地址:)

&i将引用内存中的地址,并且它会从一个索引到另一个索引递增 4,因为它保存了定义的向量类型的整数。现在&values[0]引用了第一点,当你减去 2 个地址时,两者之间的差将分别是 0,4,8,12,但实际上它减去了整数类型的大小,通常是 4 个字节。所以对应起来 0 = 0th int,4 = 1st 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;

}
Run Code Online (Sandbox Code Playgroud)

这是一个常规数组,几乎是一样的东西

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

for(auto &i: values) {

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

}
Run Code Online (Sandbox Code Playgroud)

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

  • 这只适用于紧密封装的顺序容器,例如普通数组或向量。一般来说,它“不适用于”链表、树、地图或容器。 (10认同)