我有一个输入迭代器,可以遍历文件并解析其中的记录。每个解析的记录都存储在迭代器中并归其所有(“隐藏”)。
记录并不轻量,而且数量很多,因此复制这些值是很不幸的。我希望能够将它们移出迭代器,所以我像这样定义取消引用:
Record& operator*() { return this->record; }
此代码有效:
Record r = std::move(*it); // no copy
然后我想使用适当的 C++20 概念来实现此迭代器的编译时安全性,并且我添加了:
static_assert(std::input_iterator<Iterator>);
它失败了,因为
std::input_iterator
的一部分 - std::indirectly_readable
- 要求 const Iterator
是可解引用的(相关问题:为什么简单的迭代器不可读?)。我无法让我的 operator*
在 const
上工作,因为那样它必须返回 const 引用,并且 move
实际上会复制:
const Record& operator*() const { return this->record; }
Record r = std::move(*it); // copy, 'cause can't move const objects
我发现使用
static_assert(std::input_or_output_iterator)
可以与可变取消引用一起使用。对于静态检查来说可能没问题,但我发现它很混乱,因为我的迭代器在概念上不是一个output_iterator。
另一个相关问题是
std::move_iterator
不适用于这个可变解引用运算符,因为它的 operator*()
是 const,定义为:
reference operator*() const { return ranges::iter_move(__current_); }
其中
__current_
将绑定到我的 Iterator 实例,并且在此上下文中它将具有类型 const Iterator
,iter_move 想要取消引用并失败,因为它不支持不可变的取消引用(即使它通过添加 来实现) const
,它无法正常工作,它会欺骗性地复制值而不是移动)。
实现隐藏迭代器的可移动性并使 move_iterator 工作的替代设计是什么?
迭代器的移动或复制成本较低。您不应该将
Record
保留在迭代器中。许多范围/范围适配器所做的是将其保留在范围内,并简单地将指针保留在迭代器中:
struct record_range {
private:
Record cache;
public:
struct iterator {
private:
friend record_range;
record_range* parent;
explicit iterator(record_range* parent) : parent(parent) {}
public:
using difference_type = std::ptrdiff_t;
using value_type = Record;
Record& operator*() const { return parent->cache; }
iterator& operator++() { advance_record(parent->cache); return *this; }
void operator++(int) { ++*this; }
};
friend bool operator==(iterator it, std::default_sentinel_t) {
return is_past_the_end_record(it.parent->cache);
}
iterator begin() { read_first_record(cache); return iterator{ this }; }
std::default_sentinel_t end() const { return {}; }
};
static_assert(std::input_iterator<record_range::iterator>);
static_assert(std::ranges::input_range<record_range>);