范围适配器在可视范围后面的参数方面是否懒惰?
Are range adaptors lazy in regard to arguments behind the viewable range?
C++20 标准在 [range.adaptors.general] 中表示范围适配器
evaluate lazily as the resulting view is iterated.
另一方面,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++ 迭代器模型中, 是两个不同的操作。但是,过滤迭代器的位置是 基于 访问它正在过滤的范围。这就是迭代器的本质。
要找到过滤范围的开头,需要找到基础范围中与过滤条件匹配的第一个位置。正如在过滤范围内查找下一个元素需要迭代,直到找到另一个匹配过滤条件的迭代器。
因此获取过滤范围的起始迭代器需要访问该范围的至少一个元素。过滤迭代器在完成其工作的同时尽可能做到惰性。
但是,您的特定代码显示 UB,因为您的谓词 is not a regular_invocable
。该标准明确要求:
The invoke function call expression shall be equality-preserving ([concepts.equality])
这意味着:
An expression is equality-preserving if, given equal inputs, the expression results in equal outputs. The inputs to an expression are the set of the expression's operands. The output of an expression is the expression's result and all operands modified by the expression.
您更改谓词的行为违反了该要求。
演示是最好的: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, 0, 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;
}
注意 squreIt
仅针对需要打印的项目调用,与视图顺序无关。这表明懒惰。
Is there a guarantee that adaptor will respect the value of filter variable?
没有
If not, what kind of behavior am I invoking and where is it stated?
这是 [res.on.requirements]/3 的 IFNDR:
If the semantic requirements of a declaration's constraints ([structure.requirements]) are not modeled at the point of use, the program is ill-formed, no diagnostic required.
特别是,filter_view
要求谓词求值保持相等(通过 indirect_unary_predicate
需要 predicate
,需要 regular_invocable
)。结果可以自发改变的东西不符合这个要求 - 见 [concepts.equality]/3.
C++20 标准在 [range.adaptors.general] 中表示范围适配器
evaluate lazily as the resulting view is iterated.
另一方面,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++ 迭代器模型中,
要找到过滤范围的开头,需要找到基础范围中与过滤条件匹配的第一个位置。正如在过滤范围内查找下一个元素需要迭代,直到找到另一个匹配过滤条件的迭代器。
因此获取过滤范围的起始迭代器需要访问该范围的至少一个元素。过滤迭代器在完成其工作的同时尽可能做到惰性。
但是,您的特定代码显示 UB,因为您的谓词 is not a regular_invocable
。该标准明确要求:
The invoke function call expression shall be equality-preserving ([concepts.equality])
这意味着:
An expression is equality-preserving if, given equal inputs, the expression results in equal outputs. The inputs to an expression are the set of the expression's operands. The output of an expression is the expression's result and all operands modified by the expression.
您更改谓词的行为违反了该要求。
演示是最好的: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, 0, 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;
}
注意 squreIt
仅针对需要打印的项目调用,与视图顺序无关。这表明懒惰。
Is there a guarantee that adaptor will respect the value of filter variable?
没有
If not, what kind of behavior am I invoking and where is it stated?
这是 [res.on.requirements]/3 的 IFNDR:
If the semantic requirements of a declaration's constraints ([structure.requirements]) are not modeled at the point of use, the program is ill-formed, no diagnostic required.
特别是,filter_view
要求谓词求值保持相等(通过 indirect_unary_predicate
需要 predicate
,需要 regular_invocable
)。结果可以自发改变的东西不符合这个要求 - 见 [concepts.equality]/3.