[英]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_if
, std::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;
};
我陷入了begin
和end
,等式比较等功能的实现。所以,我的问题是:
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.