为什么在使用`std::views::reverse`时对过滤函数的调用是多余的?
我试图了解通过管道运算符(视图/适配器)传递时对过滤器函数的调用顺序。我看到的结果根本不直观。虽然可能有原因,但如果有人能解决这个问题,我将不胜感激。也可以指向cppreference.com上的正确文档。
#include <vector>
#include <ranges>
#include <iostream>
int main() {
const auto vec = std::vector{1,2,3,4,5,6};
auto filter = [](const auto f) {
std::cout << "f = " << f << ", ";
return f % 2 == 0;
};
std::cout << std::endl;
for (auto v : vec
| std::views::reverse
| std::views::filter(filter)
| std::views::take(2)
| std::views::reverse)
{
std::cout << std::endl << "v = [" << v << "]" << std::endl;
}
}
实际结果:
f = 6, f = 5, f = 4, f = 3, f = 2, f = 3, f = 4,
v = [4]
f = 3, f = 4, f = 5, f = 6,
v = [6]
f = 5, f = 6,
预期结果:
f = 6, f = 5, f = 4, f = 3, f = 2, f = 1,
v = [4]
v = [6]
这是上述代码的Godbolt示例。这里还有一些代码,我试着分解它来理解。但没有什么是显而易见的。
回答
所讨论的基于范围的 for 循环可以重写为
auto&& range = vec
| std::views::reverse
| std::views::filter(filter)
| std::views::take(2)
| std::views::reverse;
auto begin = range.begin();
auto end = range.end();
for (; begin != end; ++begin) {
auto v = *begin;
std::cout << std::endl << "v = [" << v << "]" << std::endl;
}
- 的初始化程序
range只构建视图。什么都不输出。 range.begin()返回reverse_iterator其base()是一个迭代底层视图的结束。为了找到底层视图的结尾,进行了 5 次调用filter(对应于f = 6, f = 5, f = 4, f = 3, f = 2,)。range.end()返回 a ,reverse_iterator它base()是基础视图开头的迭代器。的开头filter_view已被缓存。没有调用到filter。begin != end返回真。*begin递减底层基迭代器的副本,以便访问反向范围的第一个元素。这导致接下来的 2 次调用filter(对应于f = 3, f = 4,)。- 输出第一个元素的值。(
v = [4]) ++begin递减基础基迭代器。(f = 3, f = 4,)begin != end返回真。*begin递减底层基迭代器的副本以访问第二个元素。(f = 5, f = 6,)- 输出第二个元素的值。(
v = [6]) ++begin递减基础基迭代器。(f = 5, f = 6,)begin != end返回假。
THE END
二维码