将STL算法限制为N个元素

时间:2010-11-11 11:44:11

标签: c++ stl

(受nakiya的评论启发)

许多STL算法将范围作为一对迭代器。例如,for_each(begin, end, &foo);。显然,如果distance(begin, end) >= N和begin是随机访问迭代器,则for_each(begin, begin+N, &foo);仅将foo应用于前N个元素。

如果不满足这两个条件中的任何一个,现在是否有一个干净,通用的替代方案?

3 个答案:

答案 0 :(得分:9)

在不更改迭代器类型的情况下,没有通用的完整解决方案。

证明:假设迭代器类型只是一个InputIterator,因此begin实际上是指(例如)一个流,而end是一个特殊情况的标记迭代器,它将等于真正的迭代器读取EOF后的“真实”迭代器。

然后,使用begin尝试计算end的新值以传递给算法,将“消耗”begin的原始值,因为这就是InputIterators的方式工作

你可以做的是编写一个迭代器包装类,这样迭代器就可以计算它增加了多少次,并且一旦它增加了N次就比较等于“结束”迭代器。 N可以是模板参数,也可以是一个或其他迭代器的构造函数参数。

像这样的东西。我测试它编译并为我工作。仍然要做 - 我目前只处理你的两种情况之一,“不是随机访问迭代器”。我也不处理另一个,“距离< N”。

#include <iterator>

template <typename It>
class FiniteIterator : public std::iterator<
  typename std::iterator_traits<It>::iterator_category,
  typename std::iterator_traits<It>::value_type> {
    typedef typename std::iterator_traits<It>::difference_type diff_type;
    typedef typename std::iterator_traits<It>::value_type val_type;
    It it;
    diff_type count;
  public:
    FiniteIterator(It it) : it(it), count(0) {}
    FiniteIterator(diff_type count, It it = It()) : it(it), count(count) {}
    FiniteIterator &operator++() {
        ++it;
        ++count;
        return *this;
    }
    FiniteIterator &operator--() {
        --it;
        --count;
        return *this;
    }
    val_type &operator*() const {
        return *it;
    }
    It operator->() const {
        return it;
    }
    bool operator==(const FiniteIterator &rhs) const {
        return count == rhs.count;
    }
    bool operator!=(const FiniteIterator &rhs) const {
        return !(*this == rhs);
    }
    FiniteIterator operator++(int) {
        FiniteIterator cp = *this;
        ++*this;
        return cp;
    }
    FiniteIterator operator--(int) {
        FiniteIterator cp = *this;
        --*this;
        return cp;
    }
};

请注意,第二个构造函数只接受迭代器,因为基础类型可能不是默认可构造的(如果它只是一个InputIterator)。在调用者创建“结束”迭代器的情况下,它不使用它,因为一旦另一个副本递增,它将无效。

如果底层迭代器类型是RandomAccess,则不需要/想要这个包装器。所以我提供了一个帮助模板函数,它以back_inserterback_insert_iterator的方式进行类型推导。但是,如果其参数类型是随机访问类别的迭代器,则帮助程序不应返回FiniteIterator<T>,而应仅返回T

template <typename Iterator, typename Category>
struct finite_traits2 {
    typedef FiniteIterator<Iterator> ret_type;
    static ret_type plus(Iterator it, typename std::iterator_traits<Iterator>::difference_type d) {
        return ret_type(d, it);
    }
};

template <typename Iterator>
struct finite_traits2<Iterator, std::random_access_iterator_tag> {
    typedef Iterator ret_type;
    static ret_type plus(Iterator it, typename std::iterator_traits<Iterator>::difference_type d) {
        return it + d;
    }
};

template <typename Iterator>
struct finite_traits {
    typedef typename std::iterator_traits<Iterator>::iterator_category itcat;
    typedef typename finite_traits2<Iterator, itcat>::ret_type ret_type;
    static ret_type plus(Iterator it, typename std::iterator_traits<Iterator>::difference_type d) {
        return finite_traits2<Iterator, itcat>::plus(it, d);
    }
};

template <typename Iterator, typename Distance>
typename finite_traits<Iterator>::ret_type finite_iterator(Iterator it, Distance d) {
    return finite_traits<Iterator>::plus(it, d);
}

template <typename Iterator>
typename finite_traits<Iterator>::ret_type finite_iterator(Iterator it) {
    return finite_traits<Iterator>::plus(it, 0);
}

示例用法(和最小测试):

#include <iostream>
#include <typeinfo>
#include <list>

struct MyIterator : std::iterator<std::bidirectional_iterator_tag, int> {
    difference_type count;
};

int main() {
    std::cout << typeid(MyIterator::iterator_category).name() << "\n";
    std::cout << typeid(FiniteIterator<MyIterator>::iterator_category).name() << "\n";
    std::cout << typeid(MyIterator::difference_type).name() << "\n";
    std::cout << typeid(FiniteIterator<MyIterator>::difference_type).name() << "\n";

    int a[] = {1, 2, 3, 4, 5};
    std::copy(finite_iterator(a), finite_iterator(a,4), std::ostream_iterator<int>(std::cout, " "));
    std::cout << "\n";
    std::list<int> al(finite_iterator(a), finite_iterator(a,4));
    std::cout << al.size() << "\n";
    std::copy(finite_iterator(al.begin()), finite_iterator(al.begin(),3), std::ostream_iterator<int>(std::cout, " "));
    std::cout << "\n";
}

警告:finite_iterator(x, 1) == finite_iterator(++x, 0) false ,即使对于前向迭代器或更好。有限迭代器只有在相同起点创建时才具有可比性。

此外,这仍然不完整。例如,std::reverse不起作用,因为为了访问参考,finite_iterator(x, 1)是“指向”x。

目前以下情况恰好有效:

std::list<int>::iterator e = al.begin();
std::advance(e,3);
std::reverse(finite_iterator(al.begin()), finite_iterator(e,3));

所以我不远,但这不是一个好的界面。我需要更多地考虑双向迭代器的情况。

答案 1 :(得分:5)

已经有fill_n和generate_n,没有foreach_n(或者for_n可能更合适)但是写一个很容易。

template< typename FwdIter, typename Op, typename SizeType >
void for_n( FwdIter begin, SizeType n, Op op )
{
   while( n-- )
   {
      op(*begin);
      ++begin;
   }
}

你可以做op(* begin ++)但是虽然键入较少,但它可能会生成更多代码来复制迭代器。 size_type是数字,因此后增量的效率也不低,这是一个有用的情况。

答案 2 :(得分:0)

我相信你可以创建一个类似于boost::counting_iterator的包装器迭代器类型,它将增量和底层迭代器保持在一起,并且一旦增量超过最大值就会比较等于“结束”迭代器。