如何解决为什么 range::basic_istream_view::begin() 没有缓存?
我发现 c++20 ranges::basic_istream_view
与 range-v3 版本略有不同。
最重要的区别是std::ranges::basic_istream_view
不缓存它的begin()
,这样每个begin()
都会返回下一个迭代器,带有已经读取的值({{3 }}):
auto words = std::istringstream{"today is yesterday's tomorrow"};
auto view = std::ranges::istream_view<std::string>(words);
std::cout << *view.begin() << "\n"; // today
std::cout << *view.begin() << "\n"; // is
std::cout << *view.begin() << "\n"; // yesterday's
std::cout << *view.begin() << "\n"; // tomorrow
考虑以下(godbolt),如果我使用range-v3版本,所有三个std::ranges::find()
都会找到"is"
,但是如果我使用std版本,{{ 1}} 只会在第一次调用中找到。
"is"
为什么标准选择了与 range-v3 不同的设计?如果缓存 auto words = std::istringstream{"today is yesterday's tomorrow"};
auto view = std::ranges::istream_view<std::string>(words);
std::cout << *std::ranges::find(view,"is") << "\n"; // is
std::cout << *std::ranges::find(view,"is") << "\n"; // tomorrow
std::cout << *std::ranges::find(view,"is") << "\n"; // tomorrow
,是否存在潜在缺陷?
解决方法
输入迭代器是这样的,一旦你取消引用一个,你需要立即增加它。输出迭代器也是如此,例如 back_insert_iterator。这是你不应该做的事情。如果需要缓存第一个值,请自行缓存。
解引用后输入和输出迭代器需要递增的原因是它们是设计单程的。如果您从流中读取了某些内容,则无法再次读取。运算符 * 实际上是从流中读取的。 ++有什么作用?没有什么! back_insert_iterator 也是如此
,在 range
概念的定义中,在 [range.range] 中,我们有:
template<class T>
concept range =
requires(T& t) {
ranges::begin(t); // sometimes equality-preserving (see below)
ranges::end(t);
};
“见下文”部分的位置(重点是我的):
给定表达式 t
使得 decltype((t))
为 T&
,T
模型范围仅当
- ...
- 如果
ranges::begin(t)
模型的类型为forward_iterator
,ranges::begin(t)
是保持相等的。
[注意 1:ranges::begin
和 ranges::end
的相等性保持允许将迭代器类型模型为 forward_iterator
的范围传递给多个算法并进行多次传递通过重复调用 ranges::begin
和 ranges::end
来确定范围。 由于当返回类型未对 ranges::begin
建模时,forward_iterator
不需要保持相等性,因此重复调用可能返回不相等的值或定义不明确。 — 尾注]
对于前向范围,您可以重复调用 ranges::begin(r)
并期望得到相同的答案(它是平等的)。对于某些范围,这需要缓存。
但对于仅限输入的范围(如 istream_view
),您只能调用 ranges::begin(r)
一次,并且无法保证第二次调用时会发生什么。
因此,有效程序无法观察到两种实现之间的差异,因为多次调用 begin
已经违反了此处的前提条件。
对于 istream_view
的更具体答案,range-v3 的实现也不会缓存 begin()
。这不是正在发生的差异。事实上,无论如何你都不能缓存输入迭代器,因为它们会立即失效。
区别在于当我们从流中读取第一个值时:
后者更符合一般范围模型,其中构建范围适配器实际上不做任何工作。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。