如何解决范围适配器对可视范围背后的参数是否懒惰?
C++20 标准在 [range.adaptors.general] 中说范围适配器
在迭代结果视图时惰性求值。
另一方面,有一个 remark in [range.filter.view] for filter_view's begin function 提到缓存结果。那么适配器懒到什么程度呢?
执行以下代码时:
#include <iostream>
#include <ranges>
void print(std::ranges::range auto&& r)
{
for (const auto& item : r)
{
std::cout << item << ",";
}
std::cout << " <end of range>\n";
}
int main()
{
using namespace std::ranges;
bool filter = false;
auto v = iota_view{4,10} | views::filter([&filter](auto item){return filter;});
// multipass guarantee
static_assert(std::ranges::forward_range<decltype(v)>);
filter = true;
print(v);
filter = false;
print(v);
filter = true;
print(v);
}
是否保证适配器会尊重 filter
变量的值?如果不是,我要调用什么样的行为,在哪里声明?
解决方法
请记住,在 C++ 迭代器模型中,positioning and access 是两个不同的操作。然而,过滤迭代器的位置基于访问它正在过滤的范围。这就是迭代器的本质。
要找到过滤范围的开头,需要找到与过滤条件匹配的基础范围中的第一个位置。就像在过滤范围内查找下一个元素需要迭代,直到找到另一个匹配过滤条件的迭代器。
因此获取过滤范围的起始迭代器需要访问该范围的至少一个元素。过滤迭代器在做它的工作的同时尽可能地懒惰。
但是,您的特定代码显示 UB,因为您的谓词 is not a regular_invocable
。该标准明确要求:
invoke 函数调用表达式应保持相等性 ([concepts.equality])
这意味着:
如果给定相等的输入,表达式会产生相等的输出,则该表达式是相等的。表达式的输入是表达式的操作数集。表达式的输出是表达式的结果和表达式修改的所有操作数。
您通过更改谓词的行为违反了该要求。
,是否保证适配器会尊重过滤器变量的值?
没有
如果不是,我要调用什么样的行为?在哪里声明?
这是 [res.on.requirements]/3 的 IFNDR:
如果声明约束([structure.requirements])的语义要求没有在使用时建模,则程序格式错误,不需要诊断。
特别是,filter_view
要求谓词求值是相等的(通过 indirect_unary_predicate
要求 predicate
要求 regular_invocable
)。结果可以自发改变的东西不符合此要求 - 请参阅[concepts.equality]/3。
演示是最好的:https://godbolt.org/z/WxrsfTrve
int squreIt(int x)
{
std::cout << "squreIt(" << x << ")\n";
return x * x;
}
int main()
{
std::array a{4,3,2,1,5,6};
for (auto x : a | std::views::transform(squreIt) | std::views::drop(3)) {
std::cout << "Result = " << x << '\n';
}
std::cout << "---------\n";
for (auto x : a | std::views::drop(3) | std::views::transform(squreIt)) {
std::cout << "Result = " << x << '\n';
}
return 0;
}
Note squreIt
仅针对需要打印的项目而不管视图顺序如何。这表明懒惰。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。