(受nakiya的评论启发)
许多STL算法将范围作为一对迭代器。例如,for_each(begin, end, &foo);
。显然,如果distance(begin, end) >= N
和begin是随机访问迭代器,则for_each(begin, begin+N, &foo);
仅将foo
应用于前N个元素。
如果不满足这两个条件中的任何一个,现在是否有一个干净,通用的替代方案?
答案 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_inserter
对back_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
的包装器迭代器类型,它将增量和底层迭代器保持在一起,并且一旦增量超过最大值就会比较等于“结束”迭代器。