C++20范围太多|运算符?
我对此代码使用g++10.2。有人知道为什么我在results3
的最后一个std::views::reverse
出现编译器错误吗?
#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, std::allocator<int> > >, main()::<lambda(auto:13)> >, 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>
。std::counted_iterator<R>
的iterator_traits
与部分专业化认证std::iterator_traits<std::counted_iterator<I>>
匹配。- 上述部分专业化认证源自
std::iterator_traits<I>
。 std::iterator_traits<R>
由主模板生成:提供名为iterator_category
的成员,但不提供名为iterator_concept
的成员。R
的iterator_category
与T
的相同。T
的iterator_category
为input_iterator_tag
,因为其解引用运算符不返回引用,这是C++17 ForwardIterator要求不允许的。(T
的iterator_concept
为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)
的迭代器类型在filter
从管道中移除时不是counted_iterator
;当transform
从管道中移除时iterator_category
不是input_iterator_tag
。因此result1
和result2
不会触发此错误。)
这实质上是LWG 3408。
相关文章