2012-01-25 54 views
10

在C++ 11如果我們有一個set<int> S;我們可以說:基於範圍的在C++ 11

for (auto i: S) 
    cout << i << endl; 

,但我們可以強制i是一個迭代器,我的意思是寫代碼等同於:

for (auto i = S.begin(); i != S.end(); i++) 
    cout << (i != s.begin()) ? " " : "" << *i; 

,或者我們可以做一些事情,我們可以理解的指標設置(或向量)中的i

另一個問題是我們怎麼能說,不要爲S中的所有元素做這個,但是對於其中的前半部分或除第一個之外的所有元素。

或者當我們有一個vector<int> V,並且想要打印它的第一個n值,我們應該怎麼做?我知道我們可以創建一個新的矢量,但需要時間將矢量複製到一個新的矢量。

+0

我曾問一個呈三角問題在這裏:http://stackoverflow.com/questions/8960403/get-first-n-elements-in-ac-multiset(用於多重集代替) – Cristy

+1

要打印的前n (自動val:vec | sliced(0,n)){...}'的'vector'的值。請參閱[Boost.Range中的['sliced']](http://www.boost.org/doc/libs/release/libs/range/doc/html/range/reference/adaptors/reference/sliced.html)。 –

+1

基於範圍的'for'的目的是能夠更輕鬆地遍歷整個範圍。目的是*不是*完全替代非範圍的'for'。有些東西你需要經常使用「for」;範圍'for'只是一個常用迭代模式的語法糖。它不會涵蓋一切,它不應該。 –

回答

20

不,不幸。看什麼標準說:

範圍爲基礎的聲明 的(對於範圍聲明:表達式)語句 相當於

{ 
    auto && __range = (expression); 
    for (auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin) { 
     for-range-declaration = *__begin; 
     statement 
    } 
} 

其中__range,__begin和__end是變量爲了說明僅定義

換句話說,它已經從迭代到beginend已經取消引用迭代器,你ñ永遠不會看到。

+0

有趣的是,這意味着__begin和__end必須是相同的類型。 – bcmpinc

+0

@bcmpinc:但他們可能是不同類型的?你只能迭代一個數組或者遍歷一個模板容器(不,從技術上講,你可以遍歷:: begin()和:: end()有重載的任何東西,但你明白我的意思),所以所有無論如何,包括__begin和__end在內的元素都是相同的類型。他們可能是指向派生類的指針,是的......但即使這是相同的類型,即使其中一個或另一個指針指向「不同的東西」。 – Damon

+1

使用過濾前向迭代器(即遍歷集合,但僅訪問滿足謂詞的值),迭代器檢查它是否完成(它必須這樣做)比比較它要容易得多一個結束迭代器。通過定義end()返回一些不同的虛擬類型,操作符!=可以被實現爲僅僅檢查迭代器是否完成。 (我最近實現了這樣的迭代器。) – bcmpinc

2

不,你不能。

for (... : ...) 

只是不引入新的關鍵字的原因稱爲for代替foreach。整理foreach是一個快速簡短的語法,用於迭代所有元素而不關心其索引。對於所有其他情況,簡單的for就很有效。

0

基於範圍的for適用於簡單情況。我希望在虛構東西的時候稍微有用,但是在事情真正成爲產品之前,預計它的使用通常會消失很久。它可能對初學者來說更容易,但這是一個我無法判斷的領域(但似乎推動了很多C++最近的討論)。

唯一有點建設性的方法可能是使用一個適配器,該適配器引用基礎範圍,並且其方法適當地調整迭代器。另請注意,您可能想要對處理大部分數據的循環中的第一個或最後一個元素進行特殊處理。當然,這只是另一個檢查,然後是正確預測的分支,而不是檢查,減少分支預測表的污染。

2

你不能set。使用傳統的for語法或維護自己的索引計數器。

您可以vector或其他容器與平面佈局如std::array或C風格的陣列。將其更改爲使用參考。

for (auto &i: S) 

然後你可以的i地址與s[0]地址比較,以獲得指標。

+0

什麼阻止你用'std :: set'做同樣的事情?對於(auto&i:S){if(&i ==&* S.begin()){' – MSalters

+0

@ MSalters:我想OP是要求任何'i'的索引,例如以檢測我是否處於該集合的「前半部分」。你是正確的,你可以測試索引0的特例。 –

+0

我明白它是第一個,因爲它的目的是在'N'個元素之間打印'N-1'分隔符。 – MSalters

2

對於一般情況下,你必須使用一個單獨的變量:

int i = 0; 

for (auto x : s) 
    cout << (i++ ? " " : "") << x << endl; 

有,當然,對於某些容器,如vector,但沒有工作的每個集裝箱的技巧。

爲了達到此目的,您最好使用簡單的for循環。

9

基於範圍for的原理是遍歷整個範圍。

但是決定範圍是什麼,因此你可以在範圍本身。

template <typename It> 
class RangeView { 
public: 
    typedef It iterator; 

    RangeView(): _begin(), _end() {} 
    RangeView(iterator begin, iterator end): _begin(begin), _end(end) {} 

    iterator begin() const { return _begin; } 
    iterator end() const { return _end; } 

private: 
    iterator _begin; 
    iterator _end; 
}; 

template <typename C> 
RangeView<typename C::iterator> rangeView(C& c, size_t begin, size_t end) { 
    return RangeView<typename C::iterator>(
      std::next(c.begin(), begin), 
      std::next(c.begin(), end) 
     ); 
} 

template <typename C> 
RangeView<typename C::const_iterator> rangeView(C const& c, size_t begin, size_t end) { 
    return RangeView<typename C::const_iterator>(
      std::next(c.begin(), begin), 
      std::next(c.begin(), end) 
     ); 
} 

好吧,這嚴重ressemble Boost.Range ...

現在,讓我們使用它!

for (auto i: rangeView(set, 1, 10)) { 
    // iterate through the second to the ninth element 
} 
+2

得愛看法。 +1 – Xeo

+0

@Xeo:我希望我們有更多的人:)不幸的是一些意見很簡單(像這樣),但「轉換」意見可以變得非常複雜。它們很好地適用於函數式語言(具有不變性),但「參考」思想比實現變換(綁定到臨時問題......)所需的更難。那個和迭代器很快就會變成* fat *。 –