如何直接从输入流向集合中插入值?

Zer*_*ros 2 c++ stl set c++11

如何将输入直接从输入流插入到集合容器中?

这就是我需要的

while(n--)
{
    cin>>s.emplace();
}
Run Code Online (Sandbox Code Playgroud)

假设,我需要获取 n 个输入并将容器名称设置为 's'

while(n--)
{
    int x;
    cin>>x;
    s.emplace(x);
}
Run Code Online (Sandbox Code Playgroud)

这工作正常,但我需要削减这一步。

Ted*_*gmo 7

从 C++20 开始,您可以使用、 、 和std::ranges::copystd::counted_iterator执行std::istream_iterator此操作。+使其从流中复制元素。std::default_sentinelstd::insertercounted_iteratordefault_sentineln

例子:

#include <algorithm> // ranges::copy
#include <iostream>
#include <iterator>  // counted_iterator, default_sentinel, istream_iterator, inserter
#include <set>
#include <sstream>   // istringstream - only used for the example

int main() {
    // just an example istream:
    std::istringstream cin("1 1 2 2 3 3 4 4 5 5");

    int n = 5;

    std::set<int> s;

    std::ranges::copy(
        std::counted_iterator(std::istream_iterator<int>(cin), n),
        std::default_sentinel,
        std::inserter(s, s.end())
    );

    for(auto v : s) std::cout << v << ' ';
}
Run Code Online (Sandbox Code Playgroud)

输出将只包含 3 个元素,因为流中的前 5 个元素只有 3 个唯一元素:

1 2 3
Run Code Online (Sandbox Code Playgroud)

在 C++20 之前,您可以copy_n以类似的方式使用:

std::copy_n(std::istream_iterator<int>(cin), n, std::inserter(s, s.begin()));
Run Code Online (Sandbox Code Playgroud)

注意:n如果流中实际上少于元素,则两个版本都将具有未定义的行为。众所周知,在交付您想要的内容时,流是不可预测的,并且copy_n使得错误检查变得非常困难。

为了安全起见,您可以创建一个从流中counting_istream_iterator复制最多nstd::copy元素的方法,如下所示:

std::copy(counting_istream_iterator<foo>(cin, n),
          counting_istream_iterator<foo>{},
          std::inserter(s, s.end()));
Run Code Online (Sandbox Code Playgroud)

这样的迭代器可以基于std::istream_iterator,看起来像这样:

template<class T,
         class CharT = char,
         class Traits = std::char_traits<CharT>,
         class Distance = std::ptrdiff_t>
struct counting_istream_iterator {
    using difference_type = Distance;
    using value_type = T;
    using pointer = const T*;
    using reference = const T&;
    using iterator_category = std::input_iterator_tag;
    using char_type = CharT;
    using traits_type = Traits;
    using istream_type = std::basic_istream<CharT, Traits>;

    counting_istream_iterator() : // end iterator
        isp(nullptr), count(0) {}

    counting_istream_iterator(std::basic_istream<CharT, Traits>& is, size_t n) :
        isp(&is), count(n + 1)
    {
        ++*this; // read first value from stream
    }
    counting_istream_iterator(const counting_istream_iterator&) = default;
    ~counting_istream_iterator() = default;

    reference operator*() const { return value; }
    pointer operator->() const { return &value; }

    counting_istream_iterator& operator++() {        
        if(count > 1 && *isp >> value) --count;
        else count = 0; // we read the number we should, or extraction failed
        return *this;
    }
    counting_istream_iterator operator++(int) {
        counting_istream_iterator cpy(*this);
        ++*this;
        return cpy;
    }

    bool operator==(const counting_istream_iterator& rhs) const {
        return count == rhs.count;
    }
    bool operator!=(const counting_istream_iterator& rhs) const {
        return !(*this == rhs);
    }

private:
    std::basic_istream<CharT, Traits>* isp;
    size_t count;
    T value;
};
Run Code Online (Sandbox Code Playgroud)

  • 这就是问题的答案,但值得注意的是它有缺点。这使得处理输入错误变得更加困难。如果“cin”过早到达文件结尾,您将得到“未定义的行为”。也不可能处理错误的输入,例如不可转换为“int”的输入。该解决方案简短且富有表现力,但否认任何实际的错误处理。我认为处理错误的唯一方法是启用 `cin` 的异常:[参见此。](/sf/ask/1833141061/ )但这有其自身的问题。 (4认同)