这是一个索引迭代器。主要是样板文件,省略了,因为我很懒。
template<class T>
struct indexT
//: std::iterator< /* ... */ > // or do your own typedefs, or don't bother
{
T t = {};
indexT()=default;
indexT(T tin):t(tin){}
indexT& operator++(){ ++t; return *this; }
indexT operator++(int){ auto tmp = *this; ++t; return tmp; }
T operator*()const{return t;}
bool operator==( indexT const& o )const{ return t==o.t; }
bool operator!=( indexT const& o )const{ return t!=o.t; }
// etc if you want full functionality.
// The above is enough for a `for(:)` range-loop
};
它包装了一个标量类型T,并在* 上返回一个副本。有趣的是,它也适用于迭代器,这在这里很有用,因为它可以让我们有效地从指针继承:
template<class ItA, class ItB>
struct indexing_iterator:indexT<ItA> {
ItB b;
// TODO: add the typedefs required for an iterator here
// that are going to be different than indexT<ItA>, like value_type
// and reference etc. (for simple use, not needed)
indexing_iterator(ItA a, ItB bin):ItA(a), b(bin) {}
indexT<ItA>& a() { return *this; }
indexT<ItA> const& a() const { return *this; }
decltype(auto) operator*() {
return b[**a()];
}
decltype(auto) operator->() {
return std::addressof(b[**a()]);
}
};
索引迭代器包含两个迭代器,其中第二个必须是随机访问的。它使用第一个迭代器来获取一个索引,并使用它从第二个迭代器中查找一个值。
接下来,我们有一个范围类型。在很多地方都可以找到经过 SFINAE 改进的版本。它使得在 for(:) 循环中迭代一系列迭代器变得容易:
template<class Iterator>
struct range {
Iterator b = {};
Iterator e = {};
Iterator begin() { return b; }
Iterator end() { return e; }
range(Iterator s, Iterator f):b(s),e(f) {}
range(Iterator s, size_t n):b(s), e(s+n) {}
range()=default;
decltype(auto) operator[](size_t N) { return b[N]; }
decltype(auto) operator[] (size_t N) const { return b[N]; }\
decltype(auto) front() { return *b; }
decltype(auto) back() { return *std::prev(e); }
bool empty() const { return begin()==end(); }
size_t size() const { return end()-begin(); }
};
以下是帮助轻松处理indexT 范围的助手:
template<class T>
using indexT_range = range<indexT<T>>;
using index = indexT<size_t>;
using index_range = range<index>;
template<class C>
size_t size(C&&c){return c.size();}
template<class T, std::size_t N>
size_t size(T(&)[N]){return N;}
index_range indexes( size_t start, size_t finish ) {
return {index{start},index{finish}};
}
template<class C>
index_range indexes( C&& c ) {
return make_indexes( 0, size(c) );
}
index_range intersect( index_range lhs, index_range rhs ) {
if (lhs.b.t > rhs.e.t || rhs.b.t > lhs.b.t) return {};
return {index{(std::max)(lhs.b.t, rhs.b.t)}, index{(std::min)(lhs.e.t, rhs.e.t)}};
}
好的,差不多了。
index_filter_it 采用一系列索引和一个随机访问迭代器,并将一系列索引迭代器放入该随机访问迭代器的数据中:
template<class R, class It>
auto index_filter_it( R&& r, It it ) {
using std::begin; using std::end;
using ItA = decltype( begin(r) );
using R = range<indexing_iterator<ItA, It>>;
return R{{begin(r),it}, {end(r),it}};
}
index_filter 接受一个index_range 和一个随机访问容器,将它们的索引相交,然后调用index_filter_it:
template<class C>
auto index_filter( index_range r, C& c ) {
r = intersect( r, indexes(c) );
using std::begin;
return index_filter_it( r, begin(c) );
}
现在我们有了:
for (auto&& i : index_filter( indexes(0,6), arr )) {
}
还有中提琴,我们有一个大型乐器。
live example
更高级的过滤器是可能的。
size_t filter[] = {1,3,0,18,22,2,4};
using std::begin;
for (auto&& i : index_filter_it( filter, begin(arr) ) )
将访问arr 中的 1、3、0、18、22、2、4。但是,它不会进行边界检查,除非arr.begin()[] bounds-checks。
上面的代码可能有错误,你应该使用boost。
如果您在indexT 上实现- 和[],您甚至可以菊花链这些范围。