const, span, and iterator trouble - c++

I try to write an iterator that iterates over a container by index.
A It and a const It both allow changing the content of the container.
A Const_it and a const Const_it both forbid changing the content of the container.
After that, I try to write a span<T> over a container.
For a type T that is not const, both const span<T> and span<T> allows changing the content of the container.
Both const span<const T> and span<const T> forbid changing the content of the container.
The code does not compile because:
// *this is const within a const method
// But It<self_type> requires a non-const *this here.
// So the code does not compile
It<self_type> begin() const { return It<self_type>(*this, 0); }
If I make the constructor of It to accept a const container, it doesn't look right because the iterator can modify the content of the container.
If I get rid of the const of the method, then for a non-const type T, a const span<T> cannot modify the container.
It inherits from Const_it to allow implicit conversion from It to Const_it during template instantiation.
I use a pointer instead of a reference to in the iterators (const C* container_;) for allowing assigning one iterator to another iterator.
I suspect something is very wrong here because I even think about:
Does cast away const of *this cause undefined behavior?
But I don't know how to fix it.
Test:
#include <vector>
#include <numeric>
#include <iostream>
template<typename C>
class Const_it {
typedef Const_it<C> self_type;
public:
Const_it(const C& container, const int ix)
: container_(&container), ix_(ix) {}
self_type& operator++() {
++ix_;
return *this;
}
const int& operator*() const {
return ref_a()[ix_];
}
bool operator!=(const self_type& rhs) const {
return ix_ != rhs.ix_;
}
protected:
const C& ref_a() const { return *container_; }
const C* container_;
int ix_;
};
template<typename C>
class It : public Const_it<C> {
typedef Const_it<C> Base;
typedef It<C> self_type;
public:
//It(const C& container.
It(C& container, const int ix)
: Base::Const_it(container, ix) {}
self_type& operator++() {
++ix_;
return *this;
}
int& operator*() const {
return mutable_a()[ix_];
}
private:
C& mutable_a() const { return const_cast<C&>(ref_a()); }
using Base::ref_a;
using Base::container_;
using Base::ix_;
};
template <typename V>
class span {
typedef span<V> self_type;
public:
explicit span(V& v) : v_(v) {}
It<self_type> begin() { return It<self_type>(*this, 0); }
// *this is const within a const method
// But It<self_type> requires a non-const *this here.
// So the code does not compile
It<self_type> begin() const { return It<self_type>(*this, 0); }
It<self_type> end() { return It<self_type>(*this, v_.size()); }
It<self_type> end() const { return It<self_type>(*this, v_.size()); }
int& operator[](const int ix) {return v_[ix];}
const int& operator[](const int ix) const {return v_[ix];}
private:
V& v_;
};
int main() {
typedef std::vector<int> V;
V v(10);
std::iota(v.begin(), v.end(), 0);
std::cout << v.size() << "\n";
const span<V> s(v);
for (auto&& x : s) {
x = 4;
std::cout << x << "\n";
}
}

There are two main notes to be said to make this work. First:
If I make the constructor of It to accept a const container, it doesn't look right because the iterator can modify the content of the container.
Not really, because C in your template<typename C> class It is not the actual container, but the span<V>. In other words, take a look at:
It<self_type> begin() const { return It<self_type>(*this, 0); }
Here self_type means const span<V>, therefore you are returning a It<const span<V>>. Thus, your iterator can do whatever can be done with a const span -- but the container is still non-const. The variable name container_ is not fortunate, then.
For a type T that is not const, both const span<T> and span<T> allows changing the content of the container. Both const span<const T> and span<const T> forbid changing the content of the container.
In addition, since you want that const span is allowed to modify the contents, then what you should write inside span itself is (note the const):
int& operator[](const int ix) const {return v_[ix];}
// Removing the other `const` version:
// const int& operator[](const int ix) const {return v_[ix];}
With those two bits clarified, you can then construct a working example. Here is one based from your code and simplified to solve the issue at hand:
#include <vector>
#include <iostream>
template<typename S>
class It {
typedef It<S> self_type;
const S& span_;
int ix_;
public:
It(const S& span, const int ix)
: span_(span), ix_(ix) {}
self_type& operator++() {
++ix_;
return *this;
}
int& operator*() const {
return span_[ix_];
}
bool operator!=(const self_type& rhs) const {
return &span_ != &rhs.span_ or ix_ != rhs.ix_;
}
};
template <typename V>
class span {
typedef span<V> self_type;
public:
explicit span(V& v) : v_(v) {}
It<self_type> begin() const { return It<self_type>(*this, 0); }
It<self_type> end() const { return It<self_type>(*this, v_.size()); }
int& operator[](const int ix) const {return v_[ix];}
private:
V& v_;
};
int main() {
typedef std::vector<int> V;
V v(10);
const span<V> s(v);
for (auto&& x : s) {
x = 4;
std::cout << x << "\n";
}
}
Take a look as well at the corrected implementation of operator!= and at the fact that there is no need for the non-const version of begin() and end(). You can also throw there a cbegin() and cend(). Then you have to work on adding back the const iterator cases.
By the way, in case it saves some confusion for anybody: in the near future, std::span (proposed for C++20) might be added; and it will be just a (pointer-to-first-element, index) pair -- rather than your (pointer-to-container, index) version.
In other words, as its template parameter, it will take the elements' type, rather than a container:
span<std::vector<int>> s(v);
// vs
std::span<int> s(v);
This allows consumers of std::span to avoid knowing about which container is behind the scenes (or even no container: a contiguous memory area or an array).
Finally, you may want to take a look at GSL's implementation of std::span to get some inspiration on how to fully implement it (including the second template parameter about the extent).

After studying Acorn's solution, I found another fix.
This allows using the same iterator template for both a std::vector<T> and a span.
The two cases are different.
For a non-const type T, a const std::vector<T> forbids changing its element. A const span<T> allows changing its elements.
The main difference is It<const self_type>(*this, 0); in the span class and S& span_; instead of const S& span_; in the It class.
Fix:
#include <vector>
#include <iostream>
template<typename S>
class It {
typedef It<S> self_type;
public:
It(S& span, const int ix)
: span_(span), ix_(ix) {}
self_type& operator++() {
++ix_;
return *this;
}
int& operator*() const {
return span_[ix_];
}
bool operator!=(const self_type& rhs) const {
return &span_ != &rhs.span_ or ix_ != rhs.ix_;
}
private:
S& span_;
int ix_;
};
template<typename V>
class span {
typedef span<V> self_type;
public:
explicit span(V& v) : v_(v) {}
It<const self_type> begin() const {
return It<const self_type>(*this, 0);
}
It<const self_type> end() const {
return It<const self_type>(*this, v_.size());
}
int& operator[](const int ix) const { return v_[ix]; }
private:
V& v_;
};
int main() {
// Test adding iterator to a span
typedef std::vector<int> V;
V v(10);
const span<V> s(v);
for (auto&& x : s) {
x = 4;
std::cout << x << " ";
}
std::cout << "\n";
// Test adding iterator to a std::vector
const It<V> begin(v, 0);
const It<V> end(v, v.size());
for (auto it = begin; it != end; ++it) {
*it = 10;
std::cout << *it << " ";
}
std::cout << "\n";
}

Related

Returning a range of references to a std::vector<std::unique_ptr<T>>

I have an abstract base class T and another class holding a vector of unique pointers to T. The class should support two function returning references to the entries. One of them should provide read-access, the other one should be able to modify the values in the unique_ptrs but not the pointers:
class A {
private:
std::vector<std::unique_ptr<T>> data;
public:
auto access() -> RefRange<T>;
auto const_access() -> ConstRefRange<T>;
};
The ranges should satisfy the requirements of a std::random_access_range.
How can I do this without allocating additional memory in C++17 (boost is available)?
If you have boost, much of this is done by the range adaptor indirected
class A {
private:
static auto add_const(T & t) -> const T & { return t; }
std::vector<std::unique_ptr<T>> data;
using indirected = boost::adaptors::indirected;
using transformed = boost::adaptors::transformed;
public:
auto access() { return data | indirected; }
auto const_access() const { return data | indirected | transformed(add_const); }
};
Or with std::views::transform in C++20
class A {
private:
static auto indirect(const std::unique_ptr<T> & ptr) -> T & { return *ptr; }
static auto add_const(T & t) -> const T & { return t; }
std::vector<std::unique_ptr<T>> data;
using transform = std::views::transform;
public:
auto access() { return data | transform(indirect); }
auto const_access() const { return data | transform(indirect) | transform(add_const); }
};
If you have <experimental/propagate_const>, I'd use that instead of whichever transform(add_const).
class A {
private:
std::vector<std::experimental::propagate_const<std::unique_ptr<T>>> data;
using indirected = boost::adaptors::indirected;
public:
auto access() { return data | indirected; }
auto const_access() const { return data | indirected; }
};
You can implement a wrapper type which wraps the std::vector::iterator and provides a reference to the element contained by the std::unique_ptr on dereference.
Then return a simple struct containing begin and end iterators of these types.
Ex:
template<class T>
struct MyIterator
{
typename std::vector<std::unique_ptr<T>>::iterator iter; // or const_iterator for the const version
decltype(auto) operator*() const { return *iter->get(); }
// implement the rest of the iterator functionality
// ...
};
template<class T>
struct RefRange
{
MyIterator<T> first, last;
auto begin() const { return this->first; }
auto end() const { return this->last; }
};
class A {
private:
std::vector<std::unique_ptr<T>> data;
public:
auto access() -> RefRange<T> { return { data.begin(), data.end() }; }
};
Wrapper around iterator is the first thing that comes to my mind.
It's a quick and dirty example, but you should get the idea I suppose.
Demo: https://godbolt.org/z/z365js1T9
#include <memory>
#include <vector>
#include <iostream>
#include <initializer_list>
template<typename T>
struct S
{
using VecType = std::vector<std::unique_ptr<T>>;
VecType v{};
class RefRange
{
public:
RefRange(VecType& v) : r{v} {}
struct iterator
{
typename VecType::iterator underlying_iterator;
//below just return const T& for ConstRefRange
T& operator*() const { return *underlying_iterator->get();}
iterator& operator++(){++underlying_iterator; return *this;}
iterator operator++(int) {iterator ret{*this}; ++(*this); return ret;}
friend bool operator==(const iterator& l, const iterator& r)
{
return l.underlying_iterator==r.underlying_iterator;
}
friend bool operator!=(const iterator& l, const iterator& r)
{
return !(l==r);
}
};
iterator begin() {return iterator{r.begin()};}
iterator end() {return iterator{r.end()};}
private:
VecType& r;
};
RefRange refs() {return RefRange{v};}
};
int main()
{
S<int> s;
s.v.push_back(std::make_unique<int>(5));
s.v.push_back(std::make_unique<int>(6));
s.v.push_back(std::make_unique<int>(7));
auto r = s.refs();
for (auto&& el : r) {std::cout << el;}
}

Override C++ map iterator dereference only

I have the following class that wraps a C++ map. I'd like to override just the iterator dereference, to return the map's value only instead of the key. Is this possible at all, without having to re-implement the entire std::map iterator (which I should probably avoid as much as possible)?
Here it is:
#include <map>
using std::map;
class X {
using Type = map<int, double>;
using const_iterator = typename Type::const_iterator;
public:
void insert(int key, double value) {
my_map[key] = value;
}
const_iterator cbegin() const { return my_map.cbegin(); }
const_iterator cend() const { return my_map.cend(); }
const_iterator begin() const { return my_map.cbegin(); }
const_iterator end() const { return my_map.cend(); }
private:
Type my_map;
};
int main() {
X x;
double i;
for (const auto& it : x) {
i = it.second; // works
i = it; // fails
}
}
You pretty much do need to implement an entire iterator type to provide new iterator behavior. Luckily, Boost has a couple of tools that can make this much easier: boost::iterator_facade is a tool for creating an iterator type, which takes care of satisfying all the requirement details for the various types of Iterator as defined by the Standard. And for the common case where you want to create an iterator which wraps another iterator, overriding just pieces of its functionality, there's boost::iterator_adaptor.
So you could define your X::const_iterator like this:
#include <map>
#include <boost/iterator_adaptor.hpp>
using std::map;
class X {
using Type = map<int, double>;
public:
class const_iterator : public boost::iterator_adaptor<
const_iterator, // Derived iterator type, for CRTP
typename Type::const_iterator, // Wrapped iterator type
const double> // Value type
{
public:
const_iterator() {}
private:
// Allow X to create X::const_iterator from Type::const_iterator:
explicit const_iterator(typename Type::const_iterator map_iter)
: iterator_adaptor(map_iter) {}
friend X;
// Define the dereference operation:
const double& dereference() const
{ return base()->second; }
// Allow boost's internals to use dereference():
friend boost::iterator_core_access;
};
const_iterator cbegin() const { return const_iterator(my_map.cbegin()); }
};
...
(I intentionally changed the access of the name X::const_iterator from private to public. Someone may want to explicitly name that iterator type.)
Borrowing from Yakk's answer here you can easily modify it to suite your needs.
template<class T>
T value_of(T t) { return std::move(t); }
template<class K, class V>
V value_of(std::pair<K, V> const& p) { return p.second; }
template<class It>
struct range_t {
It b;
It e;
It begin() const { return b; }
It end() const { return e; }
};
template<class T>
struct value_t {
T t;
void operator++(){ t++; }
auto operator*() { return value_of(*t); }
friend bool operator==(value_t const& left, value_t const& right)
{ return left.t == right.t; }
friend bool operator!=(value_t const& left, value_t const& right)
{ return left.t != right.t; }
};
template<class T>
range_t<value_t<T>> values_over(T b, T e) {
return {{b}, {e}};
}
template<class C>
auto values_of(C& c) {
using std::begin; using std::end;
return values_over(begin(c), end(c));
}
int main() {
X x;
double i;
for (double const& it : values_of(x)) {
i = it;
}
}

Returning different iterators with virtual derived methods

Say I have a virtual base class Base, which will in part behave like a container, with two derived classes VectorLike and RangeLike.
I want to achieve something like the following:
class VectorLike : public Base {
std::vector<int> data;
public:
virtual std::vector<int>::const_iterator cbegin() { return data.cbegin() }
virtual std::vector<int>::const_iterator cend() { return data.cend() }
}
class RangeLike : public Base {
int min, max;
class const_iterator {
int x;
public:
int operator++() { return ++x }
bool operator==( const_iterator rhs ) { return x == rhs.x }
const_iterator( int y ) { x = y }
}
public:
virtual const_iterator cbegin() { return const_iterator( min ); }
virtual const_iterator cend() { return const_iterator( max ); }
}
This code will not compile, since std::vector<int>::const_iterator and RangeLike::const_iterator aren't identical or covariant.
To achieve the second, I would need an iterator base class from which both std::vector<int>::const_iterator and RangeLike::const_iterator will derive. But then still cbegin() and cend() will have to return pointers to iterators, which will make an even bigger mess.
My question is, is it possible to achieve something like the above code and if so how?
Here is an implementation of a polymorphic const int iterator. You can construct it with any iterator type (including pointers) where std::iterator_traits<Iter>::value_type resolves to int.
This should be the case for both std::vector<int> and your_range_type<int>.
This should get you started.
#include <iostream>
#include <vector>
#include <array>
#include <memory>
#include <algorithm>
struct poly_const_iterator
{
using value_type = int;
struct concept {
virtual void next(int n) = 0;
virtual const value_type& deref() const = 0;
virtual bool equal(const void* other) const = 0;
virtual std::unique_ptr<concept> clone() const = 0;
virtual const std::type_info& type() const = 0;
virtual const void* address() const = 0;
virtual ~concept() = default;
};
template<class Iter>
struct model : concept
{
model(Iter iter) : _iter(iter) {}
void next(int n) override { _iter = std::next(_iter, n); }
const value_type& deref() const override { return *_iter; }
bool equal(const void* rp) const override { return _iter == static_cast<const model*>(rp)->_iter; }
std::unique_ptr<concept> clone() const override { return std::make_unique<model>(*this); }
const std::type_info& type() const override { return typeid(_iter); }
const void* address() const override { return this; }
Iter _iter;
};
std::unique_ptr<concept> _impl;
public:
// interface
// todo: constrain Iter to be something that iterates value_type
template<class Iter>
poly_const_iterator(Iter iter) : _impl(std::make_unique<model<Iter>>(iter)) {};
poly_const_iterator(const poly_const_iterator& r) : _impl(r._impl->clone()) {};
const value_type& operator*() const {
return _impl->deref();
}
poly_const_iterator& operator++() {
_impl->next(1);
return *this;
}
bool operator==(const poly_const_iterator& r) const {
return _impl->type() == r._impl->type()
and _impl->equal(r._impl->address());
}
bool operator != (const poly_const_iterator& r) const {
return not(*this == r);
}
};
void emit(poly_const_iterator from, poly_const_iterator to)
{
std::copy(from, to, std::ostream_iterator<int>(std::cout, ", "));
std::cout << std::endl;
}
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5 };
std::array<int, 5> a = { 6, 7,8, 9, 0 };
emit(std::begin(v), std::end(v));
emit(std::begin(a), std::end(a));
return 0;
}
expected results:
1, 2, 3, 4, 5,
6, 7, 8, 9, 0,
Here's a C++20 based solution (partial implementation)... just thought I'd put it out there.
It uses a variant with stack storage rather than polymorphism and dynamic allocation, so it might have better performance.
It should work for any given set of iterator types that have a common value_type and whose reference type is value_type&. It should also be fairly easy to adapt for other types of iterator and to include operator->.
Not claiming this is better than the implementation in the original answer. Merely an interesting alternative...
PS. normal in the names below simply indicates a basic pointer based iterator.
template <typename _Iterator, typename _Value, typename _Reference, typename _Difference>
concept forward_iterator_for
= std::forward_iterator<_Iterator>
&& std::same_as<std::iter_value_t<_Iterator>, _Value>
&& std::same_as<std::iter_reference_t<_Iterator>, _Reference>
&& std::same_as<std::iter_difference_t<_Iterator>, _Difference>;
template <typename _Iterator, typename _Value>
concept normal_forward_iterator_for //
= std::same_as<std::remove_cvref_t<_Value>, std::remove_const_t<_Value>>
&& forward_iterator_for<_Iterator, std::remove_const_t<_Value>, _Value&, std::ptrdiff_t>;
template <typename _Value, normal_forward_iterator_for<_Value> ... _Iterator>
class normal_forward_iterator_variant {
public:
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = std::remove_cv_t<_Value>;
using reference = _Value&;
using iterator_type = std::variant<_Iterator...>;
private:
iterator_type _m_iter;
public:
normal_forward_iterator_variant() = default;
template <normal_forward_iterator_for<_Value> _Iter, typename... _Args>
normal_forward_iterator_variant(std::in_place_type_t<_Iter>, _Args&&... args)
noexcept (std::is_nothrow_constructible_v<_Iter, _Args&&...>)
: _m_iter(std::in_place_type<_Iter>, std::forward<_Args>(args)...) {}
normal_forward_iterator_variant(iterator_type const& iter)
noexcept (std::is_nothrow_copy_constructible_v<iterator_type>)
: _m_iter(iter) {}
normal_forward_iterator_variant(normal_forward_iterator_variant const&) = default;
normal_forward_iterator_variant(normal_forward_iterator_variant&&) = default;
constexpr normal_forward_iterator_variant&
operator=(normal_forward_iterator_variant const& iter)
noexcept (std::is_nothrow_copy_assignable_v<iterator_type>) //
requires std::is_copy_assignable_v<iterator_type> {
_m_iter = iter._m_iter;
return *this;
}
constexpr normal_forward_iterator_variant&
operator=(normal_forward_iterator_variant&& iter)
noexcept (std::is_nothrow_move_assignable_v<iterator_type>) //
requires std::is_move_assignable_v<iterator_type> {
_m_iter = std::move(iter._m_iter);
return *this;
}
template <typename _Tp>
constexpr normal_forward_iterator_variant&
operator=(_Tp const& x)
noexcept (std::is_nothrow_assignable_v<iterator_type, _Tp&>) //
requires std::is_assignable_v<iterator_type, _Tp&> {
_m_iter = x;
return *this;
}
template <typename _Tp>
constexpr normal_forward_iterator_variant&
operator=(_Tp&& x)
noexcept (std::is_nothrow_assignable_v<iterator_type, _Tp&&>) //
requires std::is_assignable_v<iterator_type, _Tp&&> {
_m_iter = std::move(x);
return *this;
}
[[nodiscard]] constexpr reference
operator*() const noexcept {
return std::visit([](auto&& iter) -> reference {
return *iter;
}, _m_iter);
}
constexpr normal_forward_iterator_variant&
operator++() noexcept {
std::visit([](auto&& iter) {
++iter;
}, _m_iter);
return *this;
}
constexpr normal_forward_iterator_variant
operator++(int) noexcept {
normal_forward_iterator_variant rv(*this);
++*this;
return rv;
}
[[nodiscard]] friend constexpr bool
operator==(normal_forward_iterator_variant const& a, normal_forward_iterator_variant const& b) noexcept {
return (a._m_iter == b._m_iter);
}
};
Example use:
#include <iostream>
#include <iomanip>
#include <vector>
#include <list>
#include <iterator.h>
int
main() {
using vector = std::vector<int>;
using list = std::list<int>;
using iterator = normal_forward_iterator_variant<const int, vector::const_iterator, list::const_iterator>;
iterator iter;
vector v{ 0, 1, 2 };
iter = v.cbegin();
std::cout << *iter++ << std::endl;
std::cout << *iter << std::endl;
std::cout << *++iter << std::endl;
list l{ 3, 4, 5 };
iter = l.cbegin();
std::cout << *iter++ << std::endl;
std::cout << *iter << std::endl;
std::cout << *++iter << std::endl;
}
Output is 1, 2, ..., 6 as expected.

How to make custom keyword statement

How would I go about making a function that uses braces like if/for/while statements? I'm referring to this as a 'keyword statement' because I don't know what else to call it.
Meaning, for example, if I wanted to make a 'repeat' function:
repeat(3)
{
//do something
}
I guess a better question is, is this possible? If so, how would one go about doing this?
Don't do that [#define repeat] - don't try to change the syntax of the programming language you're using. That will make your code far less readable for anyone else.
You might define a range similar to a python range:
// Range
// =====
#include <iterator>
#include <utility>
template<typename T>
class Range
{
public:
typedef T value_type;
public:
class iterator
{
public:
typedef typename std::forward_iterator_tag iterator_category;
typedef typename std::size_t size_type;
typedef typename std::ptrdiff_t difference_type;
typedef T value_type;
typedef const T& reference;
typedef const T* pointer;
public:
iterator(const T& value) noexcept
: m_value(value)
{}
reference operator * () const noexcept { return m_value; }
pointer operator -> () const noexcept { return &m_value; }
iterator& operator ++ () noexcept { ++m_value; return *this; }
friend bool operator == (const iterator & a, const iterator b) noexcept {
return a.m_value == b.m_value;
}
friend bool operator != (const iterator & a, const iterator b) noexcept {
return a.m_value != b.m_value;
}
private:
T m_value;
};
public:
Range(const T& first, const T& last) noexcept
: m_first(first), m_last(last)
{}
Range(T&& first, T&& last) noexcept
: m_first(std::move(first)), m_last(std::move(last))
{}
Range(Range&& other) noexcept
: m_first(std::move(other.m_first)),
m_last(std::move(other.m_last))
{}
Range& operator = (Range&& other) noexcept {
m_first = std::move(other.m_first);
m_last = std::move(other.m_last);
return *this;
}
iterator begin() const noexcept { return m_first; }
iterator end() const noexcept { return m_last; }
private:
T m_first;
T m_last;
};
template<typename T>
inline Range<T> range(T&& first, T&& last) noexcept {
return Range<T>(std::move(first), std::move(last));
}
// Test
// ====
#include <iostream>
int main() {
for(auto i : range(0, 3))
std::cout << i << '\n';
}
A more sophisticated implementation would consider containers and iterators, too.
You could define a macro taking 1 argument:
#define repeat(COUNT) \
for (unsigned int i = 0; i < (COUNT); ++i)
and leave the brakets empty after it, the preprocessor will expand the following example:
repeat(3)
{
//do something
}
into:
for (unsigned int i = 0; i < (3); ++i)
{
//do something
}
Demo

How to correctly implement custom iterators and const_iterators?

I have a custom container class for which I'd like to write the iterator and const_iterator classes.
I never did this before and I failed to find an appropriate how-to. What are the guidelines regarding iterator creation, and what should I be aware of ?
I'd also like to avoid code duplication (I feel that const_iterator and iterator share many things; should one subclass the other ?).
Foot note: I'm pretty sure Boost has something to ease this but I can't use it here, for many stupid reasons.
Choose type of iterator which fits your container: input, output, forward etc.
Use base iterator classes from standard library. For example, std::iterator with random_access_iterator_tag.These base classes define all type definitions required by STL and do other work.
To avoid code duplication iterator class should be a template class and be parametrized by "value type", "pointer type", "reference type" or all of them (depends on implementation). For example:
// iterator class is parametrized by pointer type
template <typename PointerType> class MyIterator {
// iterator class definition goes here
};
typedef MyIterator<int*> iterator_type;
typedef MyIterator<const int*> const_iterator_type;
Notice iterator_type and const_iterator_type type definitions: they are types for your non-const and const iterators.
See Also: standard library reference
EDIT: std::iterator is deprecated since C++17. See a relating discussion here.
I'm going to show you how you can easily define iterators for your custom containers, but just in case I have created a c++11 library that allows you to easily create custom iterators with custom behavior for any type of container, contiguous or non-contiguous.
You can find it on Github
Here are the simple steps to creating and using custom iterators:
Create your "custom iterator" class.
Define typedefs in your "custom container" class.
e.g. typedef blRawIterator< Type > iterator;
e.g. typedef blRawIterator< const Type > const_iterator;
Define "begin" and "end" functions
e.g. iterator begin(){return iterator(&m_data[0]);};
e.g. const_iterator cbegin()const{return const_iterator(&m_data[0]);};
We're Done!!!
Finally, onto defining our custom iterator classes:
NOTE: When defining custom iterators, we derive from the standard iterator categories to let STL algorithms know the type of iterator we've made.
In this example, I define a random access iterator and a reverse random access iterator:
//-------------------------------------------------------------------
// Raw iterator with random access
//-------------------------------------------------------------------
template<typename blDataType>
class blRawIterator
{
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = blDataType;
using difference_type = std::ptrdiff_t;
using pointer = blDataType*;
using reference = blDataType&;
public:
blRawIterator(blDataType* ptr = nullptr){m_ptr = ptr;}
blRawIterator(const blRawIterator<blDataType>& rawIterator) = default;
~blRawIterator(){}
blRawIterator<blDataType>& operator=(const blRawIterator<blDataType>& rawIterator) = default;
blRawIterator<blDataType>& operator=(blDataType* ptr){m_ptr = ptr;return (*this);}
operator bool()const
{
if(m_ptr)
return true;
else
return false;
}
bool operator==(const blRawIterator<blDataType>& rawIterator)const{return (m_ptr == rawIterator.getConstPtr());}
bool operator!=(const blRawIterator<blDataType>& rawIterator)const{return (m_ptr != rawIterator.getConstPtr());}
blRawIterator<blDataType>& operator+=(const difference_type& movement){m_ptr += movement;return (*this);}
blRawIterator<blDataType>& operator-=(const difference_type& movement){m_ptr -= movement;return (*this);}
blRawIterator<blDataType>& operator++(){++m_ptr;return (*this);}
blRawIterator<blDataType>& operator--(){--m_ptr;return (*this);}
blRawIterator<blDataType> operator++(int){auto temp(*this);++m_ptr;return temp;}
blRawIterator<blDataType> operator--(int){auto temp(*this);--m_ptr;return temp;}
blRawIterator<blDataType> operator+(const difference_type& movement){auto oldPtr = m_ptr;m_ptr+=movement;auto temp(*this);m_ptr = oldPtr;return temp;}
blRawIterator<blDataType> operator-(const difference_type& movement){auto oldPtr = m_ptr;m_ptr-=movement;auto temp(*this);m_ptr = oldPtr;return temp;}
difference_type operator-(const blRawIterator<blDataType>& rawIterator){return std::distance(rawIterator.getPtr(),this->getPtr());}
blDataType& operator*(){return *m_ptr;}
const blDataType& operator*()const{return *m_ptr;}
blDataType* operator->(){return m_ptr;}
blDataType* getPtr()const{return m_ptr;}
const blDataType* getConstPtr()const{return m_ptr;}
protected:
blDataType* m_ptr;
};
//-------------------------------------------------------------------
//-------------------------------------------------------------------
// Raw reverse iterator with random access
//-------------------------------------------------------------------
template<typename blDataType>
class blRawReverseIterator : public blRawIterator<blDataType>
{
public:
blRawReverseIterator(blDataType* ptr = nullptr):blRawIterator<blDataType>(ptr){}
blRawReverseIterator(const blRawIterator<blDataType>& rawIterator){this->m_ptr = rawIterator.getPtr();}
blRawReverseIterator(const blRawReverseIterator<blDataType>& rawReverseIterator) = default;
~blRawReverseIterator(){}
blRawReverseIterator<blDataType>& operator=(const blRawReverseIterator<blDataType>& rawReverseIterator) = default;
blRawReverseIterator<blDataType>& operator=(const blRawIterator<blDataType>& rawIterator){this->m_ptr = rawIterator.getPtr();return (*this);}
blRawReverseIterator<blDataType>& operator=(blDataType* ptr){this->setPtr(ptr);return (*this);}
blRawReverseIterator<blDataType>& operator+=(const difference_type& movement){this->m_ptr -= movement;return (*this);}
blRawReverseIterator<blDataType>& operator-=(const difference_type& movement){this->m_ptr += movement;return (*this);}
blRawReverseIterator<blDataType>& operator++(){--this->m_ptr;return (*this);}
blRawReverseIterator<blDataType>& operator--(){++this->m_ptr;return (*this);}
blRawReverseIterator<blDataType> operator++(int){auto temp(*this);--this->m_ptr;return temp;}
blRawReverseIterator<blDataType> operator--(int){auto temp(*this);++this->m_ptr;return temp;}
blRawReverseIterator<blDataType> operator+(const int& movement){auto oldPtr = this->m_ptr;this->m_ptr-=movement;auto temp(*this);this->m_ptr = oldPtr;return temp;}
blRawReverseIterator<blDataType> operator-(const int& movement){auto oldPtr = this->m_ptr;this->m_ptr+=movement;auto temp(*this);this->m_ptr = oldPtr;return temp;}
difference_type operator-(const blRawReverseIterator<blDataType>& rawReverseIterator){return std::distance(this->getPtr(),rawReverseIterator.getPtr());}
blRawIterator<blDataType> base(){blRawIterator<blDataType> forwardIterator(this->m_ptr); ++forwardIterator; return forwardIterator;}
};
//-------------------------------------------------------------------
Now somewhere in your custom container class:
template<typename blDataType>
class blCustomContainer
{
public: // The typedefs
typedef blRawIterator<blDataType> iterator;
typedef blRawIterator<const blDataType> const_iterator;
typedef blRawReverseIterator<blDataType> reverse_iterator;
typedef blRawReverseIterator<const blDataType> const_reverse_iterator;
.
.
.
public: // The begin/end functions
iterator begin(){return iterator(&m_data[0]);}
iterator end(){return iterator(&m_data[m_size]);}
const_iterator cbegin(){return const_iterator(&m_data[0]);}
const_iterator cend(){return const_iterator(&m_data[m_size]);}
reverse_iterator rbegin(){return reverse_iterator(&m_data[m_size - 1]);}
reverse_iterator rend(){return reverse_iterator(&m_data[-1]);}
const_reverse_iterator crbegin(){return const_reverse_iterator(&m_data[m_size - 1]);}
const_reverse_iterator crend(){return const_reverse_iterator(&m_data[-1]);}
.
.
.
// This is the pointer to the
// beginning of the data
// This allows the container
// to either "view" data owned
// by other containers or to
// own its own data
// You would implement a "create"
// method for owning the data
// and a "wrap" method for viewing
// data owned by other containers
blDataType* m_data;
};
They often forget that iterator must convert to const_iterator but not the other way around. Here is a way to do that:
template<class T, class Tag = void>
class IntrusiveSlistIterator
: public std::iterator<std::forward_iterator_tag, T>
{
typedef SlistNode<Tag> Node;
Node* node_;
public:
IntrusiveSlistIterator(Node* node);
T& operator*() const;
T* operator->() const;
IntrusiveSlistIterator& operator++();
IntrusiveSlistIterator operator++(int);
friend bool operator==(IntrusiveSlistIterator a, IntrusiveSlistIterator b);
friend bool operator!=(IntrusiveSlistIterator a, IntrusiveSlistIterator b);
// one way conversion: iterator -> const_iterator
operator IntrusiveSlistIterator<T const, Tag>() const;
};
In the above notice how IntrusiveSlistIterator<T> converts to IntrusiveSlistIterator<T const>. If T is already const this conversion never gets used.
Boost has something to help: the Boost.Iterator library.
More precisely this page: boost::iterator_adaptor.
What's very interesting is the Tutorial Example which shows a complete implementation, from scratch, for a custom type.
template <class Value>
class node_iter
: public boost::iterator_adaptor<
node_iter<Value> // Derived
, Value* // Base
, boost::use_default // Value
, boost::forward_traversal_tag // CategoryOrTraversal
>
{
private:
struct enabler {}; // a private type avoids misuse
public:
node_iter()
: node_iter::iterator_adaptor_(0) {}
explicit node_iter(Value* p)
: node_iter::iterator_adaptor_(p) {}
// iterator convertible to const_iterator, not vice-versa
template <class OtherValue>
node_iter(
node_iter<OtherValue> const& other
, typename boost::enable_if<
boost::is_convertible<OtherValue*,Value*>
, enabler
>::type = enabler()
)
: node_iter::iterator_adaptor_(other.base()) {}
private:
friend class boost::iterator_core_access;
void increment() { this->base_reference() = this->base()->next(); }
};
The main point, as has been cited already, is to use a single template implementation and typedef it.
I don't know if Boost has anything that would help.
My preferred pattern is simple: take a template argument which is equal to value_type, either const qualified or not. If necessary, also a node type. Then, well, everything kind of falls into place.
Just remember to parameterize (template-ize) everything that needs to be, including the copy constructor and operator==. For the most part, the semantics of const will create correct behavior.
template< class ValueType, class NodeType >
struct my_iterator
: std::iterator< std::bidirectional_iterator_tag, T > {
ValueType &operator*() { return cur->payload; }
template< class VT2, class NT2 >
friend bool operator==
( my_iterator const &lhs, my_iterator< VT2, NT2 > const &rhs );
// etc.
private:
NodeType *cur;
friend class my_container;
my_iterator( NodeType * ); // private constructor for begin, end
};
typedef my_iterator< T, my_node< T > > iterator;
typedef my_iterator< T const, my_node< T > const > const_iterator;
There are plenty of good answers but I created a template header I use that is quite concise and easy to use.
To add an iterator to your class it is only necessary to write a small class to represent the state of the iterator with 7 small functions, of which 2 are optional:
#include <iostream>
#include <vector>
#include "iterator_tpl.h"
struct myClass {
std::vector<float> vec;
// Add some sane typedefs for STL compliance:
STL_TYPEDEFS(float);
struct it_state {
int pos;
inline void begin(const myClass* ref) { pos = 0; }
inline void next(const myClass* ref) { ++pos; }
inline void end(const myClass* ref) { pos = ref->vec.size(); }
inline float& get(myClass* ref) { return ref->vec[pos]; }
inline bool equals(const it_state& s) const { return pos == s.pos; }
// Optional to allow operator--() and reverse iterators:
inline void prev(const myClass* ref) { --pos; }
// Optional to allow `const_iterator`:
inline const float& get(const myClass* ref) const { return ref->vec[pos]; }
};
// Declare typedef ... iterator;, begin() and end() functions:
SETUP_ITERATORS(myClass, float&, it_state);
// Declare typedef ... reverse_iterator;, rbegin() and rend() functions:
SETUP_REVERSE_ITERATORS(myClass, float&, it_state);
};
Then you can use it as you would expect from an STL iterator:
int main() {
myClass c1;
c1.vec.push_back(1.0);
c1.vec.push_back(2.0);
c1.vec.push_back(3.0);
std::cout << "iterator:" << std::endl;
for (float& val : c1) {
std::cout << val << " "; // 1.0 2.0 3.0
}
std::cout << "reverse iterator:" << std::endl;
for (auto it = c1.rbegin(); it != c1.rend(); ++it) {
std::cout << *it << " "; // 3.0 2.0 1.0
}
}
I hope it helps.
I came across this post and was surprised that a simple method is not really mentioned here. Using a pointer to the value like how std::iterator describes is obviously a very generic approach. But you might be able to get away with something much simpler. Of course this is a simplistic approach and might not always be sufficient, but in case it is, I am posting it for the next reader.
Most probably the underlying type in your class is an STL container which already has defined the iterators for you. If that is the case, you can simply use their defined iterators and don't really need to make your own.
Here is an example:
class Foo {
std::vector<int>::iterator begin() { return data.begin(); }
std::vector<int>::iterator end() { return data.end(); }
std::vector<int>::const_iterator begin() const { return data.begin(); }
std::vector<int>::const_iterator end() const { return data.end(); }
private:
std::vector<int> data
};
i'm interested to know how correct this is, but seems to work as a roll-your-own iterator to internal data storage
template<typename T>
struct iterator_type
{
using self_type = iterator_type;
using iterator_category = std::random_access_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = std::remove_cv_t<T>;
using pointer = T*;
using reference = T&;
iterator_type( pointer ptr ) noexcept
: _ptr{ ptr }
{}
reference operator*() noexcept { return *_ptr; }
pointer operator->() noexcept { return _ptr; }
self_type operator++() noexcept { ++_ptr; return *this; }
self_type operator++(int) noexcept { self_type tmp = *this; ++_ptr; return tmp; }
self_type operator--() noexcept { --_ptr; return *this; }
self_type operator--(int) noexcept { self_type tmp = *this; --_ptr; return tmp; }
bool operator==( const self_type &other ) const noexcept { return _ptr == other._ptr; }
bool operator!=( const self_type &other ) const noexcept { return _ptr != other._ptr; }
private:
pointer _ptr;
};
template<typename T>
using const_iterator_type = iterator_type<std::add_const_t<T>>;
Then i just add these to my class, and seems to work as expected.
template<typename T>
class Container
{
public:
using iterator = iterator_type<T>;
using const_iterator = const_iterator_type<T>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
...
iterator begin() { return _begin; }
iterator end() { return _begin + _size; }
const_iterator cbegin() const { return _begin; }
const_iterator cend() const { return _begin + _size; }
reverse_iterator rbegin() { return reverse_iterator(_begin + _size); }
reverse_iterator rend() { return reverse_iterator(_begin); }
const_reverse_iterator crbegin() const { return const_reverse_iterator(_begin + _size); }
const_reverse_iterator crend() const { return const_reverse_iterator(_begin); }
private:
T* _begin;
size_t _size;
size_t _capacity;
};
the only thing is that to make it with the std::cbegin(), std::rcbegin(), std::cend() and std::rcend() functions I have to extend the std namespace:
namespace std
{
template<typename T>
typename Container<T>::const_iterator cbegin( Container<T> &c ) { return c.cbegin(); }
template<typename T>
typename Container<T>::const_iterator cend( Container<T> &c ) { return c.cend(); }
template<typename T>
typename Container<T>::const_reverse_iterator crbegin( Container<T> &c ) { return c.crbegin(); }
template<typename T>
typename Container<T>::const_reverse_iterator crend( Container<T> &c ) { return c.crend(); }
}
Check this below code, it works
#define MAX_BYTE_RANGE 255
template <typename T>
class string
{
public:
typedef char *pointer;
typedef const char *const_pointer;
typedef __gnu_cxx::__normal_iterator<pointer, string> iterator;
typedef __gnu_cxx::__normal_iterator<const_pointer, string> const_iterator;
string() : length(0)
{
}
size_t size() const
{
return length;
}
void operator=(const_pointer value)
{
if (value == nullptr)
throw std::invalid_argument("value cannot be null");
auto count = strlen(value);
if (count > 0)
_M_copy(value, count);
}
void operator=(const string &value)
{
if (value.length != 0)
_M_copy(value.buf, value.length);
}
iterator begin()
{
return iterator(buf);
}
iterator end()
{
return iterator(buf + length);
}
const_iterator begin() const
{
return const_iterator(buf);
}
const_iterator end() const
{
return const_iterator(buf + length);
}
const_pointer c_str() const
{
return buf;
}
~string()
{
}
private:
unsigned char length;
T buf[MAX_BYTE_RANGE];
void _M_copy(const_pointer value, size_t count)
{
memcpy(buf, value, count);
length = count;
}
};