использование std::представления::фильтр после std::представления::соединение не компилируется

#c #c 20 #std-ranges #iterator-traits

Вопрос:

У меня возникли проблемы с использованием std::views::filter объединенных потоков/диапазонов. Я не знаю, что именно здесь не так. Я полагаю, что элементы могут не передаваться с надлежащей семантикой (копирование/перемещение), следует использовать ссылочные оболочки или что-то еще.

 #include <ranges>

int main(int argc_, char* argv_[]) {
  auto r1 = std::views::single(42);

  // create a nested range to have something to join
  auto r2 = r1 | std::views::transform([](autoamp; e_) { return std::ranges::single_view{e_}; });

  auto r3 = r2 | std::views::join;

  // case #1: single -> transform -> join -> filter
  {
    auto r4a = r3 | std::views::filter([](auto e_) { return true; });

    // ERROR: missing 'iterator_category'
    for (int i : r4a) { }
  }

  // case #2: single -> transform -> join -> transform
  {
    auto r4b = r3 | std::views::transform([](auto e) { return e; });

    // OK: it compiles
    for (int i : r4b) { }
  }

  return 0;
}                                                                                          
 

Строка № 17 выдает это сообщение об ошибке:

 $ g   --version
g   (Rev6, Built by MSYS2 project) 10.2.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g   test.cpp -g -std=c  20
In file included from test.cpp:1:
d:/Programs/msys64/mingw64/include/c  /10.2.0/ranges: In instantiation of 'static constexpr auto std::ranges::filter_view<_Vp, _Pred>::_Iterator::_S_iter_cat() [with _Vp = std::ranges::join_view<std::ranges::transform_view<std::ranges::single_view<int>, main(int, char**)::<lambda(auto:13amp;)> > >; _Pred = main(int, char**)::<lambda(auto:14)>]':
d:/Programs/msys64/mingw64/include/c  /10.2.0/ranges:1473:48:   required from 'struct std::ranges::filter_view<std::ranges::join_view<std::ranges::transform_view<std::ranges::single_view<int>, main(int, char**)::<lambda(auto:13amp;)> > >, main(int, char**)::<lambda(auto:14)> >::_Iterator'
test.cpp:17:20:   required from here
d:/Programs/msys64/mingw64/include/c  /10.2.0/ranges:1455:10: error: no type named 'iterator_category' in 'struct std::iterator_traits<std::ranges::join_view<std::ranges::transform_view<std::ranges::single_view<int>, main(int, char**)::<lambda(auto:13amp;)> > >::_Iterator<false> >'
 1455 |    using _Cat = typename iterator_traits<_Vp_iter>::iterator_category;
      |          ^~~~
d:/Programs/msys64/mingw64/include/c  /10.2.0/ranges:1456:18: error: no type named 'iterator_category' in 'struct std::iterator_traits<std::ranges::join_view<std::ranges::transform_view<std::ranges::single_view<int>, main(int, char**)::<lambda(auto:13amp;)> > >::_Iterator<false> >'
 1456 |    if constexpr (derived_from<_Cat, bidirectional_iterator_tag>)
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
d:/Programs/msys64/mingw64/include/c  /10.2.0/ranges:1458:23: error: no type named 'iterator_category' in 'struct std::iterator_traits<std::ranges::join_view<std::ranges::transform_view<std::ranges::single_view<int>, main(int, char**)::<lambda(auto:13amp;)> > >::_Iterator<false> >'
 1458 |    else if constexpr (derived_from<_Cat, forward_iterator_tag>)
      |                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
d:/Programs/msys64/mingw64/include/c  /10.2.0/ranges:1459:34: error: inconsistent deduction for auto return type: 'std::bidirectional_iterator_tag' and then 'std::forward_iterator_tag'
 1459 |      return forward_iterator_tag{};
      |                                  ^
 

Что меня удивляет, так это то, что использование адаптера преобразования в объединенном потоке не вызывает никаких проблем.

Я посмотрел на реализацию диапазонов, но это просто вызвало больше вопросов. Итератор диапазонов (r1…r4 выше) имеет iterator_concepts и iterator_category типы элементов, но они, похоже, не согласуются:

 // r2) check member types directly
static_assert(std::is_same_v<typename decltype(r2.begin())::iterator_concept, std::random_access_iterator_tag>);
static_assert(std::is_same_v<typename decltype(r2.begin())::iterator_category, std::input_iterator_tag>);

// r2) check member types via iterator_traits
// why is it undefined?
// static_assert(std::is_same_v<typename std::iterator_traits<decltype(r2.begin())>::iterator_concept, ? >);
static_assert(std::is_same_v<typename std::iterator_traits<decltype(r2.begin())>::iterator_category, std::input_iterator_tag>);
                                                                                                                               
// r3) check member types directly
static_assert(std::is_same_v<typename decltype(r3.begin())::iterator_concept, std::input_iterator_tag>);                       
static_assert(std::is_same_v<typename decltype(r3.begin())::iterator_category, std::input_iterator_tag>);                      

// r3) check member types via iterator_traits
// why are these undefined?
// static_assert(std::is_same_v<typename std::iterator_traits<decltype(r3.begin())>::iterator_concept, ? >);
// static_assert(std::is_same_v<typename std::iterator_traits<decltype(r3.begin())>::iterator_category, ? >);
 

Есть ли какой-нибудь способ заставить цикл for работать в строке 17?

Комментарии:

1. Это стандартный дефект, и он устраняется с помощью P2259, в котором говорится о той же проблеме в разделе 3 . Кроме того, gcc-11 реализовал его, поэтому ваш код может быть скомпилирован в gcc-11.