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.
}
#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.
}
错误片段:
完整的错误集可以在这里看到:https : //godbolt.org/z/Y7Gjqd
回答
TL;DR:在这种情况下,结果的迭代器类型std::views::takeis 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。
- ... which will be fixed by [P2259](https://wg21.link/p2259)
- so this is modern c++ huh?
- @qwr if you can still understand how any of it works, it's not modern enough yet 🙂
回答
编辑似乎 MSVC 表现出相同的行为,所以我的答案中的结论可能不正确。
我认为这是take_view.
引用(强调我的)上的cppreference页面take_view:
take_view模型的概念contiguous_range,random_access_range,
bidirectional_range,forward_range,input_range和sized_range当底层视图V模型各自的概念。
考虑以下代码,我们看到 take range 的输入是 a 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 上写的内容相矛盾。
既然 take view 不是双向视图,则示例中的以下反向视图无法编译,因为它期望 abidirectional_range作为输入。
现场示例在这里。