我试图了解通过管道运算符(视图/适配器)传递时对过滤器函数的调用顺序。我看到的结果根本不直观。虽然可能有原因,但如果有人能解决这个问题,我将不胜感激。也可以指向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;
}
}
Run Code Online (Sandbox Code Playgroud)
实际结果:
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,
Run Code Online (Sandbox Code Playgroud)
预期结果:
f = 6, f = 5, f = 4, f = 3, f = 2, f = 1,
v = [4]
v = [6]
Run Code Online (Sandbox Code Playgroud)
所讨论的基于范围的 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;
}
Run Code Online (Sandbox Code Playgroud)
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 返回假。