如何将 N 个整数读入向量?

19

如果我想将标准输入的所有整数读入一个向量中,我可以使用以下便捷方式:

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

假设我只想读取n个整数,那么手动输入循环就是唯一的选择吗?

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

还有其他右手操作的方法吗?


1
可能是std :: copy n个元素或到结尾的重复问题。 - bartop
2个回答

12

通常情况下,您不应该使用std::copy_n进行此操作,因为它假定在将迭代器递增n次时,提供的迭代器仍然有效:

从以first开头的范围中复制count个值到以result开头的范围中。形式上,对于每个非负整数i < n,执行*(result + i) = *(first + i)

(cppreference.com上std::copy_n的文章)

如果您能够保证这一点,那么可以使用,但通常情况下,对于std::cin来说这是不可能的。您很容易使其引用无效的迭代器:

默认构造的std::istream_iterator称为流结束迭代器。当有效的std::istream_iterator到达底层流的末尾时,它变成等于流结束迭代器。进一步解除引用或递增它会调用未定义的行为。

(cppreference.com上std::istream_iterator的文章)

您的循环方式几乎可以了,不过我可能会使用更强的终止条件以避免对“无效”流进行多余读取:

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

实际上,我很想将此封装为类似于std::copy_n的东西,但接受完整的“范围”,其边界可以在从0N计数的同时进行验证。

一个实现可能如下所示:

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;
}

你可以像这样使用它:

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

现在您获得了M个元素,其中M是提供的输入数量与N中较小的那个。

(演示链接)


确认我是否正确理解这个答案,使用 copy_n 的问题在于,如果在读取 n 个元素之前流遇到问题,则行为是未定义的?所以基本上,"如果你信任你的数据源,请去尝试,但如果你不信任,请不要使用 copy_n"? - templatetypedef
基本上是这样 - 如果你“去做它”,你可以在每一步上节省一个迭代器比较。但是对于一般用途,说实话我将使用从这里开始的copy_atmost_n - Lightness Races in Orbit
请注意,我特别考虑了EOF - 说实话,如果有数据但读取/解析失败会发生什么我也不确定。 - Lightness Races in Orbit

11

如评论所述,copy_n在此任务中不安全,但您可以使用带有可变lambda的copy_if

#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;
}

正如这个答案所指出的:std::copy复制n个元素或到结尾


7
如果没有提供足够的输入,会导致未定义行为吗? - paler123
2
你无法修复它。std::copy_n 不适合这个任务。 - Lightness Races in Orbit
3
不行。对std::istream_iterator的“结束迭代器”进行解引用会导致未定义行为,而非抛出语义。(请参见我的答案) - Lightness Races in Orbit
1
@bartop 我建议删除 copy_n 部分,只需保留_如评论中所述,copy_n 是不安全的,但您可以使用带有可变 lambda 的 copy_if:_ 然后跟随您的好解决方案。 - Ted Lyngmo
使用 copy_if 版本更好(已删除反对票)。从技术上讲,不能保证谓词将按顺序调用,但我们保证它将被调用 <last-first> 次,没有充分的理由以无序方式调用它。所以这应该可以工作。不过,像我的 copy_atmost_n 这样的包装器更简洁、更具表现力,更容易正确实现。 - Lightness Races in Orbit
@LightnessRacesinOrbit 谢谢,我想我有更好的想法。 - bartop

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接