transform_view,
with some details stripped off to increase readability (e.g. namespaces).
First we define a template for the smart iterator that actually does the transformation on dereferentiation.
template <class transform,
class iter_t,
class T>
class transform_iterator
: public iterator<iterator_traits<iter_t>::iterator_category,
T,
iterator_traits<iter_t>::difference_type,
pointer_traits<T>::type,
T> {
public:
typedef iterator<iterator_traits<iter_t>::iterator_category,
T, iterator_traits<iter_t>::difference_type,
pointer_traits<T>::type,T> inherited;
typedef typename const_traits<reference>::type const_reference;
Iterators must be default constructible.
explicit transform_iterator() {}
transform_iterator(transform trans_, iter_t iter_)
: iter(iter_), trans(trans_) {}
Incrementing the transform iterators just increments the underlying iterator.
transform_iterator& operator++() {
++iter;
return *this;
}
transform_iterator operator++(int) {
transform_iterator tmp(*this);
++iter;
return *tmp;
}
Decrementing the transform iterators just decrements the underlying
iterator. Therefore the decrement operators are only instantiable if
the underlying iterator is at least bidirectional.
transform_iterator& operator--() {
--iter;
return *this;
}
transform_iterator operator--(int) {
transform_iterator tmp(*this);
--iter;
return *tmp;
}
Dereferencing the iterator does the main work, i.e. it applies the
transformation to the value in the underlying container and returns a
temporary result object.
const_reference operator*() const { return trans(*iter); }
reference operator*() { return trans(*iter); }
Iterator arithmetics is forwarded to the underlying iterator and since
we use std::advance will be done as efficiently as
possible. Note that because we determine the transform_iterator
category based on the underlying iterator category,
std::advance(transform_iter,n) will not call these operators
unless the underlying iterator supports them.
transform_iterator& operator+=(int n) {
advance(iter,n);
return *this;
}
transform_iterator operator+(difference_type n) const {
transform_iterator tmp(*this);
return tmp += n;
}
transform_iterator& operator-=(int n) {
advance(iter,-n);
return *this;
}
transform_iterator operator-(difference_type n) const {
transform_iterator tmp(*this);
return tmp -= n;
}
const_reference operator[](difference_type n) const {
return *(*this + n);
}
difference_type operator-(const transform_iterator& y ) const {
if (iter < y.iter) return -std::distance(iter,y.iter);
else return std::distance(y.iter,iter);
}
Comparison operators are templated to allow comparison between mutable
and immutable iterator variants. More exactly, the underlying
iterators must be comparable.
template <class iter2, class T2>
bool operator==(const transform_iterator<transform,
iter2,T2>& rhs) const {
return iter==rhs.iter;
}
template <class iter2, class T2>
bool operator< (const transform_iterator<transform,
iter2,T2>& rhs) const {
return iter < rhs.iter;
}
Conversion from mutable to immutable iterator. Instantiation requires
the conversion from iter_t to const_iter and T to const_T.
template<class const_iter, class const_T>
operator transform_iterator<transform,const_iter,
const_T>() const {
return transform_iterator<transform,const_iter,
const_T>(trans,iter);
}
The smart iterator provides access to the underlying iterator.
iter_t base() const { return iter; }
The smart iterator encapsulates an underlying iterator as well as the
transformation to be applied.
private: iter_t iter; transform trans; template<class trans2, class iter2, class T2> friend class transform_iterator; };
Second, the view template itself is defined. It provides type definitions for its iterator types, iterator generation methods and meta data methods.
template <class container,
class transformation,
class const_tag,
class ownership_tag,
class T>
class transform_view {
public:
typedef one_container_base<container,
const_tag,ownership_tag> proxy_t;
typedef T value_type;
typedef typename const_traits<T>::type const_value_type;
typedef typename ctor_arg<container,const_tag,
ownership_tag>::type ctor_arg_type;
typedef view_traits<container,const_tag> vt;
typedef typename vt::container_type domain_type;
typedef typename vt::iterator domain_iterator;
typedef typename vt::const_iterator domain_const_iterator;
typedef transformation transform_type;
typedef typename pointer_traits<T>::type pointer;
typedef T reference;
typedef typename const_traits<T>::type const_reference;
typedef typename domain_type::size_type size_type;
typedef typename domain_type::difference_type difference_type;
typedef transform_iterator<transform_type,
domain_iterator,
value_type> iterator;
typedef transform_iterator<transform_type,
domain_const_iterator,
const_value_type> const_iterator;
typedef reverse_iterator<iterator> reverse_iterator;
typedef reverse_iterator<const_iterator> const_reverse_iterator;
Views must be default constructible, although a default constructed
views is good for nothing except subsequent assignment.
explicit transform_view() {}
transform_view(ctor_arg_type& cont, const transform_type& tr)
: proxy(cont), trans(tr) {}
The iterator factory methods follow the standard container interface.
const_iterator begin() const {
return const_iterator(trans,proxy.cont().begin());
}
const_iterator end() const {
return const_iterator(trans,proxy.cont().end());
}
iterator begin() {
return iterator(trans,proxy.cont().begin());
}
iterator end() {
return iterator(trans,proxy.cont().end());
}
Reverse iterator factory methods require the underlying container to
be reversible.
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
reverse_iterator rbegin() {
return reverse_iterator(end());
}
reverse_iterator rend() {
return reverse_iterator(begin());
}
The usual container meta information is provided as well.
size_type size() const { return proxy.cont().size(); }
size_type max_size() const { return proxy.cont().max_size(); }
bool empty() const { return proxy.cont().empty(); }
If the underlying container is a sequence or back insertion sequence,
the front() and back() methods can be instantiated.
const_reference front() const {
return trans(proxy.cont().front());
}
const_reference back() const {
return trans(proxy.cont().back());
}
reference front() {
return trans(proxy.cont().front());
}
reference back() {
return trans(proxy.cont().back());
}
Subscription operators require the underlying container to be random accessable.
const_reference operator[](size_type n) const {
return trans(proxy.cont()[n]);
}
reference operator[](size_type n) {
return trans(proxy.cont()[n]);
}
const_reference at(size_type n) const {
range_check(n);
return (*this)[n];
}
reference at(size_type n) {
range_check(n);
return (*this)[n];
}
Erasing elements from the view is done by erasing them in the
underlying container. Inserting elements would require an inverse
transformation.
iterator erase(iterator first, iterator last) {
return iterator(proxy.cont().erase(first.base(),last.base()));
}
Popping elements from the ends of the view requires the underlying
container to be a front or back insertion sequence.
void pop_front() { proxy.cont().pop_front(); }
void pop_back() { proxy.cont().pop_back(); }
A swap method easing the definition of the free standing swap function.
void swap(transform_view& b) {
swap(proxy,b.proxy);
swap(trans,b.trans);
}
The transform view encapsulates (a reference to) the underlying
container and the transformation to be applied.
private:
proxy_t proxy;
transformation trans;
void range_check(size_type n) const {
if (n < 0 || n >= size())
throw std::range_error("transform_view");
}
};
Finally, views have to be equality and less than comparable. For this purpose we define non-member comparison operators.
template <class container_1,class container_2,
class transformation_1,class transformation_2,
class const_tag_1,class const_tag_2,
class ownership_tag_1,
class ownership_tag_2,
class T_1,class T_2>
bool operator==(transform_view<container_1,
transformation_1,
const_tag_1,
ownership_tag_1,T_1> const & lhs,
transform_view<container_2,
transformation_2,
const_tag_2,
ownership_tag_2,T_2> const & rhs) {
return lhs.size() == rhs.size() &&
equal(lhs.begin(), lhs.end(), rhs.begin());
}
template <class container_1,class container_2,
class transformation_1,class transformation_2,
class const_tag_1,class const_tag_2,
class ownership_tag_1,
class ownership_tag_2,
class T_1,class T_2>
bool operator<(transform_view<container_1,
transformation_1,
const_tag_1,
ownership_tag_1,T_1> const & lhs,
transform_view<container_2,
transformation_2,
const_tag_2,
ownership_tag_2,T_2> const & rhs) {
return lexicographical_compare(lhs.begin(),lhs.end(),
rhs.begin(),rhs.end());
}
Views must also be swapable.
template <class container,
class transformation,
class const_tag,
class ownership_tag,
class T>
void swap(transform_view<container,transformation,
const_tag,ownership_tag,T>& a,
transform_view<container,transformation,
const_tag,ownership_tag,T>& b) {
a.swap(b);
}