有没有办法在C ++ 11基于范围的for循环中访问迭代器(假设没有循环索引 ..?)?
通常我们需要对容器的第一个元素做一些特殊的事情并迭代其余的元素
我在这个伪代码中寻找像 c++11_get_index_of
这样的语句:
for (auto& elem: container)
{
if (c++11_get_index_of(elem) == 0)
continue;
// do something with remaining elements
}
我真的想避免在那种情况下回到old-style manual iterator handling代码。
答案 0 :(得分:23)
通常我们需要对a的第一个元素做一些特别的事情 容器并迭代剩余的元素。
我很惊讶地发现到目前为止还没有人提出这个解决方案:
auto it = std::begin(container);
// do your special stuff here with the first element
++it;
for (auto end=std::end(container); it!=end; ++it) {
// Note that there is no branch inside the loop!
// iterate over the rest of the container
}
它有一个很大的优势,即分支移出循环。它使循环更简单,也许编译器也可以更好地优化它。
如果你坚持使用基于范围的for循环,也许最简单的方法是这样做(还有其他更丑陋的方法):
std::size_t index = 0;
for (auto& elem : container) {
// skip the first element
if (index++ == 0) {
continue;
}
// iterate over the rest of the container
}
但是,如果您只需跳过第一个元素,我会认真地将分支移出循环。
答案 1 :(得分:2)
不,你不能在基于范围的for
循环中获取迭代器(当然没有查找容器中的元素)。迭代器由标准定义为名为__begin
,但这仅用于展示。如果需要迭代器,则可以使用普通的for
循环。基于范围的for
循环存在的原因是针对那些您不需要自己处理迭代的情况。
使用auto
和std::begin
以及std::end
,您的for
循环应该仍然非常简单:
for (auto it = std::begin(container); it != std::end(container); it++)
答案 2 :(得分:2)
如何在迭代器中使用简单的for循环:
for(auto it = container.begin(); it != container.end(); it++)
{
if(it == container.begin())
{
//do stuff for first
}
else
{
//do default stuff
}
}
它不是基于范围的,但它是有效的。 如果您可能仍想使用范围循环:
int counter = 0;
for(auto &data: container)
{
if(counter == 0)
{
//do stuff for first
}
else
{
//do default stuff
}
counter++;
}
答案 3 :(得分:2)
当迭代元素时,总是更喜欢使用algorithm,并且只有在没有算法适合的情况下才使用普通的for
循环。
选择正确的算法取决于你想要对元素做什么...你没有告诉我们。
如果要跳过第一个元素,请转储示例:
if (!container.empty()) {
for_each(++container.begin(), container.end(), [](int val) { cout << val; });
}
答案 4 :(得分:2)
Boost提供了一个很好的简洁方法:
std::vector<int> xs{ 1, 2, 3, 4, 5 };
for (const auto &x : boost::make_iterator_range(xs.begin() + 1, xs.end())) {
std::cout << x << " ";
}
// Prints: 2 3 4 5
您可以在make_iterator_range
标题中找到boost/range/iterator_range.hpp
。
答案 5 :(得分:0)
如果没有迭代器,指针或侵入索引,就无法知道元素在容器中的距离。这是一种简单的方法:
int index= 0;
for (auto& elem: container)
{
if (index++ == something)
continue;
// do something with remaining elements
}
如果要跳过第一个元素,另一种方法是使用std :: deque和pop_front作为第一个元素。然后你可以像往常一样用容器循环你的ranged。
答案 6 :(得分:0)
当我需要在随机访问容器上做这样的事情时,我的习惯是迭代索引。
for( std::size_t i : indexes( container ) ) {
if (i==0) continue;
auto&& e = container[i];
// code
}
唯一棘手的部分是编写indexes
,它返回一系列boost调用counting
迭代器。从迭代器创建一个基本的可迭代范围很简单:使用boost的范围概念,或者自己动手。
任意迭代器类型的基本范围是:
template<typename Iterator>
struct Range {
Iterator b; Iterator e;
Range( Iterator b_, Iterator e_ ):b(b_), e(e_) {};
Iterator begin() const { return b; }
Iterator end() const { return e; }
};
你可以讨价还价,但那是核心。
答案 7 :(得分:0)
我会尽量避免使用迭代器,因为 range-based for loop 的想法是摆脱它们。从 C++20 开始,要跳过 container
中的第一个元素,我将采用以下方法之一。为了完整起见,我还包括如何单独处理第一个元素:
处理循环外的第一个元素
您可以使用所有 sequence containers 都存在的 container.front()
来访问第一个元素。但是,您必须确保容器不为空以避免分段错误。然后,要跳过循环中的第一个(或更多)元素,您可以使用 std::views::drop
中的范围适配器 Ranges library。总的来说,它看起来如下:
std::vector<int> container { 1, 2, 3 };
if(!container.empty()) {
// do something with first element
std::cout << "First element: " << container.front() << std::endl;
}
for (auto& elem : container | std::views::drop(1)) {
// do something with remaining elements
std::cout << "Remaining element: " << elem << std::endl;
}
您还可以使用另一个基于范围的 for 循环和范围适配器 std::views::take(1)
来代替 container.front()
。 take()
和 drop()
的优点是即使它们的参数超过 container
中元素的数量,它们也能安全工作。
处理循环内的第一个元素
您可以使用 init-statement in a range-based for loop 来定义布尔标志(甚至是计数器)。这样,标志仅在循环范围内可见。您可以在循环内使用标志,如下所示:
std::vector<int> container { 1, 2, 3 };
for(bool isFirst(true); auto& elem : container) {
if(isFirst) {
// do something with first element
std::cout << "First element: " << elem << std::endl;
isFirst = false;
continue;
}
// do something with remaining elements
std::cout << "Remaining element: " << elem << std::endl;
}
显示两种方法的输出:
<块引用>第一个元素:1
剩余元素:2
剩余元素:3