将next(),hasNext()迭代器接口转换为begin(),end()接口

时间:2017-11-20 23:52:19

标签: c++ iterator c++14

我必须使用外部库我无法改变。此库可以通过其内部逻辑对特殊格式的文件进行标记。 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实现迭代器包装器,允许使用标准algorithms librarystd::copy_ifstd::count等)。更具体地说,如果迭代器包装器满足input iterator的要求就足够了。

我目前的试用版如下:

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*()的重载而不是引用返回一个值,这是一个好方法吗?

2 个答案:

答案 0 :(得分:4)

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

我发现它大大减少了代码所需的样板数量。

然后,您必须决定如何表示已经到达" end"的迭代器。一种方法是使默认构造的迭代器成为" end"迭代器。它不包含任何对象,您不得增加或取消引用它。 "开始"然后,iterator是一个非默认构造的迭代器。它有一个对象,您可以取消引用它。递增此迭代器只需检查hasNext()。如果为true,则将包含的对象设置为next()。如果为false,则清除包含的对象并使此迭代器看起来像默认构造的。

operator*开始按价值返回有任何问题。即使您指定了引用,生命周期扩展也会保持值,直到引用超出范围。也就是说,任何假设此类引用在多次迭代中保持有效的代码都会中断,因此请坚持使用简单的for (auto val : tokens)for (auto& val : tokens)

答案 1 :(得分:1)

根据accepted answer的建议,我成功实现了我打算做的迭代器包装。

这是一个示例实现,对应于问题中显示的示例:

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;
};

为了与range-based for loop兼容,以下是必要的begin()end()功能:

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