[英]How can I skip elements in a range-based for loop based on 'index'?
有沒有辦法在基於 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
}
在那種情況下,我真的很想避免回到舊式手動迭代器處理代碼。
通常我們需要對容器的第一個元素做一些特殊的事情,並迭代剩余的元素。
我很驚訝地看到到目前為止沒有人提出這個解決方案:
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
}
但是,如果您只需要跳過第一個元素,我會認真地將分支移出循環。
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
您可以在boost/range/iterator_range.hpp
頭文件中找到make_iterator_range
。
不,您無法在基於范圍的for
循環中獲取迭代器(當然,無需查找容器中的元素)。 迭代器由標准定義為名為__begin
但這僅用於說明。 如果您需要迭代器,則打算使用普通的for
循環。 基於范圍的for
循環存在的原因是那些您不需要自己處理迭代的情況。
使用auto
和std::begin
和std::end
,您的for
循環應該仍然非常簡單:
for (auto it = std::begin(container); it != std::end(container); it++)
如何在迭代中使用簡單的 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++;
}
在迭代元素時,總是更喜歡使用algorithm ,並且只有在沒有任何算法適合時才使用普通的for
循環。
選擇正確的算法取決於您想對元素做什么……您還沒有告訴我們。
如果要跳過第一個元素,請轉儲示例:
if (!container.empty()) {
for_each(++container.begin(), container.end(), [](int val) { cout << val; });
}
如果沒有迭代器、指針或侵入性索引,就無法知道元素在容器內的距離。 這是一個簡單的方法:
int index= 0;
for (auto& elem: container)
{
if (index++ == something)
continue;
// do something with remaining elements
}
如果要跳過第一個元素,另一種方法是使用 std::deque 和 pop_front 第一個元素。 然后你可以像往常一樣用容器做你的 ranged for 循環。
當我需要在隨機訪問容器上做這樣的事情時,我的習慣是遍歷索引。
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; }
};
你可以把它弄得一團糟,但這是核心。
我會盡量避免使用迭代器,因為基於范圍的 for 循環的想法是擺脫它們。 從C++20 開始,要跳過container
的第一個元素,我將采用以下方法之一。 為了完整起見,我還包括如何單獨處理第一個元素:
處理循環外的第一個元素
您可以使用所有 序列容器都存在的container.front()
來訪問第一個元素。 但是,您必須確保容器不為空以避免分段錯誤。 然后,要跳過循環中的第一個(或更多)元素,您可以使用Ranges library 中的范圍適配器std::views::drop
。 總而言之,它看起來如下:
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; }
除了container.front()
您還可以將另一個基於范圍的 for 循環與范圍適配器std::views::take(1)
。 take()
和drop()
的優點是即使它們的參數超過container
中元素的數量,它們也能安全地工作。
處理循環內的第一個元素
您可以在基於范圍的 for 循環中使用init 語句來定義布爾標志(甚至計數器)。 這樣,標志僅在循環范圍內可見。 您可以在循環內使用該標志,如下所示:
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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.