2017-06-28 36 views
3

我喜歡的for循環在C++中的範圍內,希望使用這樣的:這是製作迭代器的可接受方式嗎?

#include <bits/stdc++.h> 

int main() 
{ 
    for (auto s : LineReader("my-very-big-textfile.txt")) { 
     cout << s << endl; 
    } 
    return 0; 
} 

的這裏的目的是通過一些數據進行迭代(沒有讀取所有到容器第一)。在這種情況下,文本字符串是文本文件中的行。但通常它可以是任何東西(包括生成的數據)。

這裏LineReader返回一個可迭代的「僞」容器。爲了使其工作,for循環需要LineReader對象的迭代器。在C++中,範圍是以開始和結束迭代器的形式定義的。但是我想使用範圍for-loop來循環遍歷開始時可能不知道結尾的數據(例如,爲了查找結束而不必先通過它來讀取(超大文本文件中的行)行)。

所以我定義是這樣的:

免責聲明:示例代碼顯示的原則,所以因此我不會「糾纏」,它與過度使用的std ::的,錯誤處理,私有/公共關鍵字和所以...

struct ReadLineIterator { 
    ifstream ifs; 
    string line; 

    ReadLineIterator() { } 
    ReadLineIterator(string filename) : ifs(filename) { } 

    bool operator!=(ReadLineIterator& other) { 
     return !ifs.eof(); 
    } 

    ReadLineIterator& operator++() { 
     getline(ifs, line, '\n'); 
     return *this; 
    } 
    string operator*() { 
     return line; 
    } 
}; 

struct LineReader 
{ 
    string filename; 
    LineReader(const string& filename) : filename(filename) { } 

    ReadLineIterator begin() 
    { 
     return ReadLineIterator(filename); 
    } 

    ReadLineIterator end() // return a not used dummy iterator since this method must exist 
    { 
     return ReadLineIterator(); 
    } 
}; 

當我運行這個,它的工作原理。但我很懷疑,如果

bool operator!=(ReadLineIterator& other) { 
    return !ifs.eof(); 
} 

是使這個運營商檢測序列結束的正確方法。這是因爲我沒有任何適當的結束對象(end()方法只是返回一個虛擬迭代器),並且沒有對它進行比較。相反,我檢查流是否爲空。

但我不明白我怎麼能用其他方式做到這一點?現在我很滿意這種做法,因爲它適用於我,但知道是否有更好的方法來做同樣的事情會很棒。此外,如果能夠與所有(C++)編譯器一起工作(我正在使用GCC),並且如果能夠與未來的C++標準一起工作,那麼迭代器的處理方式可能會有所不同,這將非常高興。

+0

當我看完你的問題的其餘部分時,讓我開始說,不要包含'bits /'文件:它們是非標準的,不可移植的。改爲包含所需的標準標題。 –

+3

不,它不是一個實現迭代器的正確方法,因爲「it!= it」通常會返回「true」。 –

+1

這不是有效的迭代器。你必須有typedefs和一些其他的操作符。另外,正如你注意到的那樣,'operator!='不檢查'other'迭代器。請參閱[這裏](https://stackoverflow.com/questions/8054273/how-to-implement-an-stl-style-iterator-and-avoid-common-pitfalls/8054856#8054856)瞭解如何製作適當的迭代器。看起來你想要一個'input_iterator'。 –

回答

2

我會這樣做的兩部分。

一個是range類,只是充當一個流迭代器的包裝:

template <class T> 
class istream_range { 
    std::istream_iterator<T> b; 
    std::istream_iterator<T> e; 
public: 
    istream_range(std::istream &is) 
     : b(std::istream_iterator<T>(is)) 
     , e(std::istream_iterator<T>()) 
    {} 

    std::istream_iterator<T> begin() { return b; } 
    std::istream_iterator<T> end() { return e; } 
}; 

因此,這使得我們可以使用istream_iterator S IN for循環基於範圍的:

for (auto const &s : istream_range<foo>(myfile)) 
    // do something with s 

istream_iterator使用operator>>從指定文件中提取項目,因此第二部分只是一種提取一行的微小類型:

class line { 
    std::string data; 
public: 
    friend std::istream &operator>>(std::istream &is, line &l) { 
     std::getline(is, l.data); 
     return is; 
    } 
    operator std::string() const { return data; }  
}; 

所以,用這個我們for循環變得像:

for (auto const &s : istream_range<line>(myfile)) 
    // do something with s 

這樣做的明顯優勢是解耦二:我們可以使用istream_range<T>處理的T文件,對於任何T正常流提取做「正確的事情」(包括許多我們目前無法意識到的自定義提取器)。

previous question(包括LineInputIterator似乎更接近您要求的答案)的答案中涵蓋了幾種可能性。

+0

似乎缺少istream_range初始值設定項列表中的一些右括號,我添加了該項,然後該解決方案運行並實現了我想要的結果。自然地,我必須在將sd :: string提供給std :: cout之前將其明確地轉換爲std :: string。 **非常感謝您的解釋。** –

+0

@VisitorIterator:感謝您的錯誤報告 - 我相信我已經糾正了錯別字。很高興它是有幫助的。 –

1

標準模板類std::istream_iterator<T>充當從一個IStream讀取連續的T對象(與operator>>(istream &, T &))迭代器,所以你需要的是一個類型T,從一個IStream讀取行:

class line { 
    std::string line; 
    friend std::istream &operator>>(std::istream &in, line &l) { 
     return std::getline(in, l.line); 
    } 
public: 
    operator std::string() const { return line; } 
}; 

現在有您的LineReader只返回std::istream_iterator<line>

相關問題