为什么std :: generate()和std :: generate_n()需要不同的迭代器?

时间:2017-07-22 11:44:53

标签: c++ iterator language-lawyer

我在cppreference中查看了generate()generate_n(),并且我正在尝试理解为什么generate()需要ForwardIterator,而generate_n()需要OutputIterator对于范围? (我检查了标准的最新工作草案,这是相同的要求。)

因为,至少他们可能的实现似乎需要相同的迭代器概念,而OutputIterator似乎就足够了:

generate()

template<class ForwardIt, class Generator>
void generate(ForwardIt first, ForwardIt last, Generator g)
{
    while (first != last) {
        *first++ = g();
    }
}

generate_n()

template<class OutputIt, class Size, class Generator>
OutputIt generate_n(OutputIt first, Size count, Generator g)
{
    for (Size i = 0; i < count; i++) {
        *first++ = g();
    }
    return first;
}

std::fill()std::fill_n()相同的故事。

3 个答案:

答案 0 :(得分:37)

  

至少他们可能的实现似乎需要相同的迭代器概念,而OutputIterator似乎已经足够了

OutputIterator不支持平等/不平等比较(包括您展示的operator!=的可能实施中使用的generate())和多通道保证,而ForwardIterator则。这意味着OutputIterator无法通过[first, last)接口所需的两个迭代器(例如generate())来表示范围。

  

可能无法为输出迭代器定义平等和不等式。即使定义了运算符==,x == y也不需要暗示++ x == ++ y。

答案 1 :(得分:9)

songyuanyao's answer从技术角度解释了这个问题。我会尝试提供一些非正式的解释。

许多STL算法(包括generatefill)都应用于多个项目。算法必须能够访问这些项的方式定义了迭代器的要求。

在您的情况下,generate的定义包含:

...
while (first != last) {  // implies that Iter implements operator!=
  *first++;              // implies that Iter implements operator++

虽然任何迭代器类型似乎都满足了第二个要求(毕竟,这是迭代器的全部内容 - 迭代事物:)),对比较operator!=的支持不是所有迭代器类型。

例如,您无法将ostream_iterator用于std::generate。但是,您可以例如通过std::generate_n将固定数量的生成值输出到流中。

此处a very artificial example at Coliru。一旦我开始考虑实际应用程序,我猜测使用OutputIterators的能力可能对实现某些序列化逻辑很有用。

答案 2 :(得分:8)

与所有标准库算法一样,

generate()generate_n()范围上运行,即通过迭代器访问的一系列值。为了将操作应用于范围的所有元素,算法必须知道范围的开始位置和结束位置。有两种常见的方法可以为它提供信息:您可以使用迭代器和长度指定范围,并使用while (length-- != 0) { ... ++first; }形式的循环;或者您可以使用一对迭代器[first, last)指定范围,并使用while (first != last) { ... ++first; }形式的循环。

对于第一个版本,您需要能够递增迭代器,并且对于这些算法,通过迭代器写入值。这些是输出迭代器的主要原因,这就是generate_n()所需的一切。

对于第二个版本,您需要能够增加迭代器并通过迭代器写入值,就像第一个版本一样。你必须能够比较两个迭代器的相等性,输出迭代器不支持它;你必须至少有一个前进迭代器。这就是generate()占用一对迭代器指定的范围的原因,需要一个前向迭代器。