Использование диапазонов C ++ 20 с istreambuf_iterator - PullRequest
4 голосов
/ 18 июня 2020

Я не могу скомпилировать (очень надуманный) Пример диапазонов C ++ :

#include <ranges>
#include <fstream>
#include <vector>

template <typename R>
auto populate(R&& range)
{
    return std::vector<char>(range.begin(), range.end());
}

int main(int argc, char* argv[]) {
    auto stream = std::ifstream{"/etc/hosts"};

    const auto is_odd = [](auto i) { return (i % 2) == 1; };
    const auto hosts_data = populate(
        std::ranges::subrange{std::istreambuf_iterator<char>{stream},
                              std::istreambuf_iterator<char>{}} |
        std::views::filter(is_odd)
    );

    return EXIT_SUCCESS;
}

Результатов:

<source>:19:35:   required from here
<source>:9:17: error: no matching function for call to 'std::vector<char>::vector(std::ranges::filter_view<std::ranges::subrange<std::istreambuf_iterator<char, std::char_traits<char> >, std::istreambuf_iterator<char, std::char_traits<char> >, std::ranges::subrange_kind::unsized>, main(int, char**)::<lambda(auto:13)> >::_Iterator, std::ranges::filter_view<std::ranges::subrange<std::istreambuf_iterator<char, std::char_traits<char> >, std::istreambuf_iterator<char, std::char_traits<char> >, std::ranges::subrange_kind::unsized>, main(int, char**)::<lambda(auto:13)> >::_Iterator)'

    9 |     return std::vector<char>(range.begin(), range.end());
      |   

Дальнейшие эксперименты показали что использование istreambuf_iterator вызывает проблему, но я не знаю почему. Кто-нибудь может помочь?

1 Ответ

4 голосов
/ 18 июня 2020

Это меня огорчает.

filter_view::iterator определяется как [range.filter.iterator] :

constexpr iterator& operator++();
constexpr void operator++(int);
constexpr iterator operator++(int) requires forward_range<V>;

Примечательно, что subrange из istreambuf_iterator<char> не forward_range - это input_range (потому что istreambuf_iterator - это только input_iterator).

Таким образом, постфикс operator++ возвращает void вместо iterator.

В результате наш конкретный filter_view::iterator соответствует не требованиям cpp17-iterator, как указано в [iterator.traits] / 2 :

template<class I>
concept cpp17-iterator =
  copyable<I> && requires(I i) {
    {   *i } -> can-reference;
    {  ++i } -> same_as<I&>;
    { *i++ } -> can-reference; // we fail this one
  };

Конструктор, который вы пытаетесь вызвать в vector, указан как from [vector.overview] :

template<class InputIterator>
  constexpr vector(InputIterator first, InputIterator last, const Allocator& = Allocator());

Это означает, что из [sequence.reqmts] / 13 :

Если конструктор

template<class InputIterator>
  X(InputIterator first, InputIterator last,
    const allocator_type& alloc = allocator_type());

вызывается с типом InputIterator, не квалифицируется как итератор ввода, тогда конструктор не должен участвовать в разрешении перегрузки.

И наш тип итератора не квалифицируется как итератор ввода, потому что результат постфиксного приращения не разыменовывается (см. также эту таблицу ).

По крайней мере, это всего лишь итератор ввода, так что никакого выигрыша в эффективности в любом случае не будет, поэтому вы можете просто изменить свою реализацию populate() на if constexpr в зависимости от того, действительно ли вы получаете cpp17- итератор из него (на основе проверки is_constructible) и просто loop / push_back в противном случае.

По какой-то причине ranges::to from range-v3 здесь не компилируется, но я думаю, что это проблема компилятора .

...