为啥我需要另一个迭代器作为 std::copy() 中的参数?

Posted

技术标签:

【中文标题】为啥我需要另一个迭代器作为 std::copy() 中的参数?【英文标题】:Why do I need another iterator as an argument in std::copy()?为什么我需要另一个迭代器作为 std::copy() 中的参数? 【发布时间】:2019-09-10 08:02:43 【问题描述】:

我不明白为什么我需要在调用std::copy() 时将另一个迭代器作为第二个参数来读取文件。文件的迭代器“结束”如何结束?

    vector<Point> v;
    istream_iterator<Point> is(file), end;
    copy(is, end, back_inserter(v));

【问题讨论】:

一个范围需要分:开始和结束。如果没有结束,std::copy 怎么知道什么时候停止复制? 【参考方案1】:

文件的迭代器'end'如何结束?

按照惯例和/或标准库中的设计决定。迭代器end 是默认构造的,在cppreference 上,我们了解了默认的std:istream_iterator 构造函数:

constexpr istream_iterator();

构造流结束迭代器,对存储的值进行值初始化[...]

更深层的原因是标准算法是围绕半开范围的概念构建的,通常表示为[first, last)is 迭代器必须与某种结束标记 last 区分开来 - 否则,std::copy 无法知道何时从输入中读取不再有意义(即,何时到达文件末尾)。在你的情况下,这是end

【讨论】:

也就是说,它只需要另一个迭代器作为比较? 是的。查看std::copy 的示例实现:主循环将firstlast 进行比较,其中last 是您的end【参考方案2】:

为什么?

您需要以某种方式告诉算法它应该复制多少个元素。请注意,copy 是通用的,使用迭代器的原因是与实际容器无关。因此,算法在到达容器末端时无法停止。它所拥有的只是你传递给它的两个迭代器。

怎么做?

这就是它在语言中的定义方式。如果您查看不带参数的构造函数,您会发现它创建了一个特殊的流结束迭代器。来自cppreference:

constexpr istream_iterator();    (1) 

1) 构造流结束迭代器,对存储的值进行值初始化。如果定义 auto x = T(); 中的初始值设定项,则此构造函数为 constexpr;是一个常量初始化器。

【讨论】:

【参考方案3】:

我相信问题是问为什么std::copy 在文件中遇到实际的EOF(文件结尾)字符时不只是停止复制,而不是人为地传入默认构造的方法迭代器。

std::copy 是一种通用算法,适用于许多具有迭代器的容器。从向量或数组复制需要开始和结束输入迭代器,因为在向量和数组中没有对应的 EOF 标记。由于我们想对文件使用相同的copy 程序,因此文件迭代器只是用于此目的,并且实现可能会执行类似

的操作

如果文件迭代器指向EOF,则将其设置为默认构造值

这当然是微不足道的。当然,可以有一个类似file_copy(file, destination)的函数,但是它会是一个不同于我们习惯的标准std::copy的接口,并且我们将对文件使用不同的copy 函数,就像我们对数组和向量所做的那样。

【讨论】:

以上是关于为啥我需要另一个迭代器作为 std::copy() 中的参数?的主要内容,如果未能解决你的问题,请参考以下文章

std::copy性能分析与memmove机器级实现

为啥在 std::copy 期间使用 std::back_inserter 而不是 end()?

为啥将迭代器作为参数传递并在尾部位置递归时后缀失败并且前缀工作正常? [复制]

使用 std::copy 复制数组时出现分段错误

空大括号 作为范围的结尾

为啥返回一个迭代器低耦合(OOP)?