如何规避输出迭代器的迭代?

时间:2013-05-10 11:51:16

标签: c++ iterator

我在下面实现的算法是众所周知的Robert Floyd算法,该算法从总数为N的数组中返回M个随机数。该算法返回一组元素,但在算法中,您需要遍历此结果集以检查之前找到的元素是否已添加到结果集中。

不可能遍历输出迭代器,因为它在文档中指出输出迭代器只应解除引用一次。

template<typename Iter, typename RandomGenerator>
Iter random_element(Iter start, Iter end, RandomGenerator& g) {
    if (start == end) return start;
    std::uniform_int_distribution<> dis(0, std::distance(start, end) - 1);
    std::advance(start, dis(g));
    return start;
}

template<typename Iter>
Iter random_element(Iter start, Iter end) {
    static std::random_device rd;
    static std::mt19937 gen(rd());
    return random_element(start, end, gen);
}

//! @brief Algorithm of Robert Floyd.
template<typename InputIterator, typename OutputIterator>
OutputIterator random_n(InputIterator first, InputIterator last, OutputIterator result, size_t number) {
    // "misuse" the glibc functions to enforce the notions conform to the documentation
    typedef typename std::iterator_traits<InputIterator>::value_type ValueType;
    __glibcxx_function_requires(_InputIteratorConcept<InputIterator>);
    __glibcxx_function_requires(_OutputIteratorConcept<OutputIterator, ValueType>);
    __glibcxx_requires_valid_range(first1, last1);

    if (first == last) return result;
    if (number == 0) return result;
    assert (number <= (last - first));

    // create container to store distances, not the value itself, neither the iterator values
    std::vector<size_t> distance;
    InputIterator j = last - number + 1;

    // in the case of number=1, j will need to be the end of the array, so full array is searched
    while (j <= last) {
        InputIterator rand_index = random_element(first,j);
        size_t rand = std::distance(first, rand_index);
        if (std::find(distance.begin(), distance.end(), rand) != distance.end()) {
            distance.push_back(std::distance(first,j) - 1);
        } else {
            distance.push_back(rand);
        }
        ++j;
    }
    // fill result container
    for (size_t i = 0; i < distance.size(); ++i) {
        *result = *(first+distance[i]);
        ++result;
    }
    return result;
}

当前解决方案创建一个临时向量,该向量存储相对于迭代器first的距离,最后使用这些距离一次填充result数组。虽然看起来很难看。是否有一些特殊的迭代器结构可用于处理无法在输出迭代器上多次循环的事实?

2 个答案:

答案 0 :(得分:3)

您可以收紧算法的要求,并要求ForwardIterator指向输出。

答案 1 :(得分:1)

你的功能可以做任何想做的事。然后,您需要向用户指定如何使用模板化参数。

名称OutputIterator只是一个标识符;它没有引入标准的任何限制或功能。它是一种文档形式,因此如果您进行第二次传递并使用迭代器作为输入,OutputIterator将是一个误导性的名称。

根据标准,ForwardIterator需要多次通过保证,您可以保留迭代器的先前值并多次读取其引用的对象,并且仍然继续获取相同的值,此外,基础序列仍然存在。所有这些似乎都是必要的,足以满足您的目的因此,您可以调用模板参数ForwardIterator。但它仍然只是一个名字。在实施更严格的系统之前,C ++模板使用duck typing

标准建议并命名某些通用接口,但任何事情都可以。