How to read N integers into a vector?

2019-03-14 15:01发布

问题:

If I want to read all integers from standard input to a vector, I can use the handy:

vector<int> v{istream_iterator<int>(cin), istream_iterator()};

But let's assume I only want to read n integers. Is the hand-typed loop everything I got?

vector<int> v(n);
for(vector<int>::size_type i = 0; i < n; i++)
    cin >> v[i];

Or is there any more right-handed way to do this?

回答1:

As given in comments, copy_n is unsafe for this job, but you can use copy_if with mutable lambda:

#include <iterator>
#include <vector>
#include <iostream>
#include <algorithm>

int main(){
    const int N = 10;
    std::vector<int> v;
    //optionally v.reserve(N);
    std::copy_if(
        std::istream_iterator<int>(std::cin),
        std::istream_iterator<int>(), 
        std::back_inserter(v), 
        [count=N] (int)  mutable {
            return count && count--;
    });

    return 0;
}

as pointed out in this answer: std::copy n elements or to the end



回答2:

You usually shouldn't do this with std::copy_n, which assumes that the provided iterator, when incremented n times, remains valid:

Copies exactly count values from the range beginning at first to the range beginning at result. Formally, for each non-negative integer i < n, performs *(result + i) = *(first + i).

(cppreference.com article on std::copy_n)

If you can guarantee that, then fine, but generally with std::cin that's not possible. You can quite easily have it dereferencing an invalid iterator:

The default-constructed std::istream_iterator is known as the end-of-stream iterator. When a valid std::istream_iterator reaches the end of the underlying stream, it becomes equal to the end-of-stream iterator. Dereferencing or incrementing it further invokes undefined behavior.

(cppreference.com article on std::istream_iterator)

You're pretty much there with your loop, though I'd probably use stronger termination condition to avoid excess reads from a "dead" stream:

vector<int> v(n);
for(vector<int>::size_type i = 0; i < n; i++)
    if (!cin >> v[i])
       break;

I'd be tempted actually to wrap this into something that's like std::copy_n, but accepts a full "range" whose bounds may be validated in addition to counting from 0 to N.

An implementation might look like:

template<class InputIt, class Size, class OutputIt>
OutputIt copy_atmost_n(InputIt first, InputIt last, Size count, OutputIt result)
{
   for (Size i = 0; i < count && first != last; ++i)
      *result++ = *first++;
   return result;
}

You'd use it like this:

copy_atmost_n(
   std::istream_iterator<int>(std::cin),
   std::istream_iterator<int>(),
   N,
   std::back_inserter(v)
);

Now you get M elements, where M is either the number of inputs provided or N, whichever is smaller.

(live demo)



标签: c++ vector input