如何解决C++20 范围太多 |运营商?
我在这段代码中使用了 g++ 10.2。有人知道为什么我在 std::views::reverse
上的最后一个 results3
出现编译器错误吗?
#include <vector>
#include <ranges>
int main() {
auto values = std::vector{1,2,3,4,5,6,7,8,9,10};
auto even = [](const auto value) {
return value % 2 == 0;
};
auto square = [](const auto value) {
return value * value;
};
auto results1 = values
| std::views::filter(even)
| std::views::reverse
| std::views::take(4)
| std::views::reverse;
auto results2 = values
| std::views::transform(square)
| std::views::reverse
| std::views::take(4)
| std::views::reverse;
auto results3 = values
| std::views::filter(even)
| std::views::transform(square)
| std::views::reverse
| std::views::take(4)
| std::views::reverse; // Error happens on this line.
}
错误片段:
...
<source>: In function 'int main()':
<source>:30:9: error: no match for 'operator|' (operand types are 'std::ranges::take_view<std::ranges::reverse_view<std::ranges::transform_view<std::ranges::filter_view<std::ranges::ref_view<std::vector<int,std::allocator<int> > >,main()::<lambda(auto:13)> >,main()::<lambda(auto:14)> > > >' and 'const std::ranges::views::__adaptor::_RangeAdaptorClosure<std::ranges::views::<lambda(_Range&&)> >')
25 | auto results3 = values
| ~~~~~~
26 | | std::views::filter(even)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~
27 | | std::views::transform(square)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
28 | | std::views::reverse
| ~~~~~~~~~~~~~~~~~~~~~
29 | | std::views::take(4)
| ~~~~~~~~~~~~~~~~~~~~~
| |
| std::ranges::take_view<std::ranges::reverse_view<std::ranges::transform_view<std::ranges::filter_view<std::ranges::ref_view<std::vector<int,main()::<lambda(auto:14)> > > >
30 | | std::views::reverse;
| ^ ~~~~~~~~~~~~~~~~~~~
| |
| const std::ranges::views::__adaptor::_RangeAdaptorClosure<std::ranges::views::<lambda(_Range&&)> >
...
可以在此处查看完整的错误集:https://godbolt.org/z/Y7Gjqd
解决方法
TL;DR:在这种情况下,std::views::take
的结果的迭代器类型是 std::counted_iterator
,它有时无法在预期不会失败的情况下对迭代器概念进行建模。这是 LWG 3408,由 P2259 解决。
这涉及到一些非常复杂的机制。
让
-
T
是values | std::views::filter(even) | std::views::transform(square)
的迭代器类型, -
R
变成std::reverse_iterator<T>
。
在 result3
的初始化器中:
-
... | take(4)
的迭代器类型为std::counted_iterator<R>
。 -
iterator_traits
的std::counted_iterator<R>
与部分特化std::iterator_traits<std::counted_iterator<I>>
匹配。 - 所述部分特化派生自
std::iterator_traits<I>
。 -
std::iterator_traits<R>
从主模板生成:它提供名为iterator_category
的成员,但不提供名为iterator_concept
的成员。 -
iterator_category
的R
与T
的相同。 -
iterator_category
的T
是input_iterator_tag
,因为它的解引用运算符不返回引用,这是 C++17 ForwardIterator 要求所不允许的。 (iterator_concept
的T
是bidirectional_iterator_tag
。)
所以最后std::iterator_traits<std::counted_iterator<R>>
没有提供iterator_concept
,它的iterator_category
是input_iterator_tag
。
因此,... | take(4)
的结果无法对 bidirectional_range
建模,因此被 views::reverse
拒绝。
(当从管道中移除 ... | take(4)
时,counted_iterator
的迭代器类型不是 filter
;当 iterator_category
时 input_iterator_tag
不是 transform
}} 已从管道中删除。因此 result1
和 result2
不会触发此错误。)
这本质上是 LWG 3408。
,EDIT 似乎 MSVC 表现出相同的行为,所以我的答案中的结论可能不正确。
我认为这是 take_view
实现中的一个错误。
引用 take_view
上的 cppreference 页面(强调我的):
take_view 对概念 contiguous_range、random_access_range、 bidirectional_range、forward_range、input_range 和 sized_range 当 底层视图 V 为各自的概念建模。
考虑以下代码,我们看到 take range 的输入是一个 bidirectional_range
:
auto input_to_take = values
| std::views::filter(even)
| std::views::transform(square)
| std::views::reverse;
static_assert(std::ranges::bidirectional_range<decltype(input_to_take)>); // No error here.
但是,在将该范围传递到 take_view
之后,它不再是 bidierctional_range
。
auto t = take_view(input_to_take,4);
static_assert(std::ranges::bidirectional_range<decltype(t)>); // Error (constraints not satisfied)
我认为这与 cppreference 上写的内容相矛盾。
既然获取视图不是双向视图,则示例中的以下反向视图无法编译,因为它需要 bidirectional_range
作为输入。
现场示例here。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。