将 C++20 范围与 istreambuf_iterator 结合使用

cma*_*t85 4 c++ c++20

我无法编译(非常人为的)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;
}
Run Code Online (Sandbox Code Playgroud)

结果是:

<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());
      |   
Run Code Online (Sandbox Code Playgroud)

从进一步的实验来看,似乎是使用istreambuf_iterator导致了问题,但我不知道为什么。有人可以帮忙吗?

Bar*_*rry 5

这让我很难过。


filter_view::iterator被指定为,来自[range.filter.iterator]

constexpr iterator& operator++();
constexpr void operator++(int);
constexpr iterator operator++(int) requires forward_range<V>;
Run Code Online (Sandbox Code Playgroud)

值得注意的是, a subrangeofistreambuf_iterator<char>不是 a forward_range- 它是一个input_range(因为istreambuf_iterator只是一个input_iterator)。

因此,后缀 operator++返回void而不是iterator

因此,我们的特殊性filter_view::iterator不满足[iterator.traits]/2中指定要求:cpp17-iterator

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
  };
Run Code Online (Sandbox Code Playgroud)

您尝试调用的构造函数vector被指定为,来自[vector.overview]

template<class InputIterator>
  constexpr vector(InputIterator first, InputIterator last, const Allocator& = Allocator());
Run Code Online (Sandbox Code Playgroud)

这意味着,来自[sequence.reqmts]/13

如果构造函数

template<class InputIterator>
  X(InputIterator first, InputIterator last,
    const allocator_type& alloc = allocator_type());
Run Code Online (Sandbox Code Playgroud)

使用InputIterator不符合输入迭代器资格的类型调用,则构造函数不应参与重载决策。

并且我们的迭代器类型不符合输入迭代器的资格,因为后缀增量的结果不可取消引用(另请参阅此表)。


至少这只是一个输入迭代器,所以无论如何都不会提高效率,因此您可以根据是否实际从中获得 cpp17-iterator (基于检查)来更改populate()to的实现(基于检查)并循环/否则。if constexpris_constructiblepush_back

由于某种原因ranges::to, range-v3 无法在这里编译,但我认为这是编译器问题。