C++20范围太多运营商?

我正在为此代码使用 g++ 10.2。没有任何人知道为什么我得到一个编译错误在过去std::views::reverseresults3

#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)
  • Rstd::reverse_iterator<T>

在 的初始化程序中result3

  1. 的迭代器类型... | take(4)std::counted_iterator<R>
  2. iterator_traitsstd::counted_iterator<R>部分特匹配std::iterator_traits<std::counted_iterator<I>>
  3. 所述部分专业化源自std::iterator_traits<I>
  4. std::iterator_traits<R>从主模板生成:它提供名为 的成员iterator_category,但不提供名为的成员iterator_concept
  5. iterator_categoryR的相同T
  6. 所述iterator_categoryTinput_iterator_tag,因为它的引用操作不返回参考,这是不通过C ++ 17 ForwardIterator要求允许的。(iterator_conceptTbidirectional_iterator_tag。)

所以最后,std::iterator_traits<std::counted_iterator<R>>不提供iterator_concept,它iterator_categoryinput_iterator_tag

因此, 的结果... | take(4)无法建模bidirectional_range,因此被 拒绝views::reverse

(的迭代器类型... | take(4)不是counted_iteratorfilter从管道除去的iterator_categoryinput_iterator_tagtransform从管道除去左右。result1result2不触发该错误。)

这本质上是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作为输入。

现场示例在这里。


以上是C++20范围太多运营商?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>