在 c++20 的范围库中,所有视图都有第二个版本。
根据cppreference:
表达式views::filter(E, P) 的表达式等价于 filter_view{E, P} 对于任何合适的子表达式 E 和 P。
我们有实际的类,
std::ranges::filter_view
,然后放入我们拥有的视图命名空间中std::ranges::views::filter
。两者之间有什么区别,还是纯粹是风格问题?
所有视图都有第二个版本
不,他们没有。
filter_view
是一种类型。 filter
是一个 函子。通过调用 filter
函子,您可以创建相应 filter_view
类型(或接近足够类型)的对象。
但是您也可以仅使用一个参数来调用
filter
函子:过滤谓词。此类调用的返回值是一个对象,您可以 |
针对范围来 also 创建一个 filter_view
等效项,如您链接到的页面上所述:
ints | std::views::filter(even)
您不能直接使用
filter_view
来做到这一点。
因此,您可以使用常见的 C++ 表示法创建过滤视图:
filter_view(range, predicate)
,也可以使用更实用的样式谓词表示法:range | views::filter(predicate)
。
补充 Nicol Bolas 的答案:Barry Revzin 的博客文章“更喜欢
views::meow
”(2023-03-14)对此进行了很好的介绍。
Nicol 指出你可以说
std::views::filter(isEven)
,但不能说std::ranges::filter_view(isEven)
。那些不考虑语法的视图呢?仍然有理由更喜欢,例如std::views::all(rg)
超过std::ranges::ref_view(rg)
?是的,因为 std::views::all(rg)
并不总是创建 ref_view
。 如果 rg
已经是一个视图(例如 filter_view
),那么 views::all(rg)
将只是 be(的副本) rg
,而不是成为ref_view
包裹(参考)rg
。
template<std::ranges::range R>
auto very_bad(R&& rg) {
return std::ranges::ref_view(rg);
}
template<std::ranges::range R>
auto still_not_great(R&& rg) {
return std::views::all(rg);
}
auto goofus = very_bad(std::views::iota(1, 10));
auto gallant = still_not_great(std::views::iota(1, 10));
这里
gallant
是一个 std::ranges::iota_view<int, int>
,(在本例中)非常漂亮。但goofus
是一个悬空的std::ranges::ref_view<std::ranges::iota_view<int, int>>
,这是UB的使用方式。 (当然,这是一个人为的情况;实际上,您永远不应该从 any 函数返回 Ranges 视图。也就是说,still_not_great
...仍然不是很好。但是您可以看到 UB 的潜力是如何相关的所涉及类型的复杂性使用views::all
可以使复杂性低于替代方案。)
Barry 还展示了
views::reverse
与 reverse_view
的示例:
std::vector<int> v = {1,2,3};
auto rv = v | std::views::reverse;
auto goofus = std::ranges::reverse_view(rv);
auto gallant = std::views::reverse(rv);
这里
rv
是 v
颠倒过来的:它的元素序列为 {3,2,1}。
那么
gallant
是 rv
的逆过程:它的元素又是 {1,2,3}。
但是
goofus
实际上正在使用CTAD(这是邪恶的),所以goofus
只是rv
的直接副本。 goofus
的元素仍保持 {3,2,1} 的顺序。这几乎肯定不是您想要的!
TLDR:总是更喜欢
views::meow
——这个好听的简称,Just Works。切勿手动实例化 ranges::meow_view
— 更麻烦的名称,有时会出错。