我有一个对象向量,并使用range-for循环迭代它。我用它来打印对象中的函数,如下所示:
vector<thisObject> storedValues;
//put stuff in storedValues
for(auto i:storedValues)
{
cout<<i.function();
}
但我也要打印索引。我想要的输出是:
1: value
2: value
//etc
我打算只使用我每次增加的计数器,但这看起来非常低效。还有更好的方法吗?
答案 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-v3
。 Range-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}}函数不同。
您可以看到在线示例here。 ranges::v3::view::zip
的文档为here,源代码本身已托管here。如果您使用的是MSVC编译器,也可以查看here。
答案 3 :(得分:5)
您可以使用range-v3的enumerate
视图:
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