在 istreambuf_iterator 中使用 C++20 范围

Using C++20 ranges with istreambuf_iterator

我无法编译一个(非常做作的)C++ ranges example:

#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 引起的,但我不知道为什么。有人可以帮忙吗?

这让我很难过。


filter_view::iterator 指定为,从 [range.filter.iterator]:

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

值得注意的是,istreambuf_iterator<char>subrange 不是 forward_range - 它是 input_range(因为 istreambuf_iterator 只是 input_iterator).

因此,postfixoperator++returnsvoid代替了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 中调用的构造函数被指定为来自 [vector.overview]:

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

也就是说,从[sequence.reqmts]/13:

If the constructor

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

is called with a type InputIterator that does not qualify as an input iterator, then the constructor shall not participate in overload resolution.

并且我们的迭代器类型符合输入迭代器的条件,因为后缀增量的结果不可解引用(另请参见this table)。


至少这只是一个输入迭代器,因此无论如何都不会提高效率,因此您可以根据是否将 populate() 的实现更改为 if constexpr您实际上从中得到了一个 cpp17 迭代器(基于检查 is_constructible),否则只是循环/push_back

出于某种原因,range-v3 中的 ranges::to 无法在此处编译,但我认为这是编译器问题。