簡體   English   中英

將next(),hasNext()迭代器接口轉換為begin(),end()接口

[英]Turning the next(), hasNext() iterator interface into begin(), end() interface

我必須使用外部庫我無法改變。 此庫可以通過其內部邏輯對特殊格式的文件進行標記。 tokenizer提供了一個用於訪問令牌的迭代器接口,類似於以下簡化示例:

class Tokenizer {
public:
    /* ... */
    Token token() const; // returns the current token
    Token next() const; // returns the next token
    bool hasNext() const; // returns 'true' if there are more tokens
    /* ... */
};

我想為呈現的Tokenizer實現一個迭代器包裝器,允許使用標准算法庫std::copy_ifstd::count等)。 更具體地說,如果迭代器包裝器滿足輸入迭代器的要求就足夠了。

我目前的試用版如下:

class TokenIterator {
public:
    using iterator_category = std::input_iterator_tag;
    using value_type = Token;
    using difference_type = std::ptrdiff_t;
    using pointer = const value_type*;
    using reference = const value_type&;

    explicit TokenIterator(Tokenizer& tokenizer) :
            tokenizer(tokenizer) {
    }
    TokenIterator& operator++() {
        tokenizer.next();
        return *this;
    }
    value_type operator*() {
        return tokenizer.token();
    }

private:
    Tokenizer& tokenizer;
};

我陷入了beginend ,等式比較等功能的實現。所以,我的問題是:

  • 如何構造指示令牌序列結束的TokenIterator實例(即hasNext() == false ),如何將其與另一個TokenIterator實例進行比較以確定它們是否相同?
  • 如果我從operator*()的重載而不是引用返回一個值,這是一個好方法嗎?

首先,我建議您仔細查看http://www.boost.org/doc/libs/1_65_1/libs/iterator/doc/iterator_facade.html

我發現它大大減少了像這樣的代碼所需的樣板數量。

然后,您必須決定如何表示已達到“結束”的迭代器。 一種方法是使默認構造迭代器成為“結束”迭代器。 它不包含任何對象,您不得增加或取消引用它。 然后,“begin”迭代器是一個非默認構造的迭代器。 它有一個對象,您可以取消引用它。 遞增此迭代器只需檢查hasNext() 如果為true,則將包含的對象設置為next() 如果為false,則清除包含的對象並使此迭代器看起來像默認構造的迭代器。

operator*返回價值不存在任何問題。 即使您指定了引用,生命周期擴展也會保持值,直到引用超出范圍。 也就是說,任何假設此類引用在多次迭代中保持有效的代碼都會中斷,因此堅持簡單for (auto val : tokens)for (auto& val : tokens)

通過接受的答案的建議,我成功實現了我打算做的迭代器包裝器。

這是一個示例實現,對應於問題中顯示的示例:

class TokenIterator {
public:
    using iterator_category = std::input_iterator_tag;
    using value_type = Token;
    using difference_type = std::ptrdiff_t;
    using pointer = const value_type*;
    using reference = const value_type&;

    TokenIterator() : tokenizer(nullptr), token(value_type()) {
    }
    TokenIterator(Tokenizer& tokenizerToWrap) : TokenIterator() {
        if(tokenizerToWrap.hasNext()) {
            tokenizer = &tokenizerToWrap;
            token = tokenizerToWrap.token();
        }
    }
    TokenIterator(const TokenIterator& other) :
            tokenizer(other.tokenizer), token(other.token) {
    }
    reference operator*() const {
        assertTokenizer();
        return token;
    }
    pointer operator->() const {
        return &(operator*());
    }
    TokenIterator& operator++() {
        assertTokenizer();
        if(tokenizer->hasNext())
            token = tokenizer->next();
        else
            *this = TokenIterator();
        return *this;
    }
    TokenIterator operator++(int) {
        TokenIterator previousState = *this;
        operator++();
        return previousState;
    }
    friend bool operator==(const TokenIterator& lhs, const TokenIterator& rhs) {
        return lhs.tokenizer == rhs.tokenizer;
    }
    friend bool operator!=(const TokenIterator& lhs, const TokenIterator& rhs) {
        return !(lhs == rhs);
    }

private:
    void assertTokenizer() const {
        if(!tokenizer) throw std::out_of_range("iterator is out of range");
    }

    Tokenizer* tokenizer;
    value_type token;
};

為了與基於范圍的for循環兼容,以下是必要的begin()end()函數:

TokenIterator begin(Tokenizer& tokenizerToWrap) {
    return TokenIterator(tokenizerToWrap);
}
TokenIterator end(Tokenizer&) {
    return TokenIterator();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM