1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
|
#include <iostream>
#include <string>
#include <iterator>
// A simple forward-iterator, working for arrays (academic exercise).
template < typename T >
class Iterator // : public std::iterator< std::forward_iterator_tag, T > (discouraged by C++17)
{
T* m_hook ; // points to item
public:
// The requirements of a standard-library compatible iterator includes:
// std::iterator_traits<Iterator> has the member typedefs value_type,
// difference_type, reference, pointer, and iterator_category
// see: http://en.cppreference.com/w/cpp/concept/Iterator
// http://en.cppreference.com/w/cpp/iterator/iterator_traits
// one way to implement this in C++14 was to inherit publicly from std::iterator<>
// for example, for this iterator, from std::iterator< std::forward_iterator_tag, T >
// for good reasons, this is discouraged by C++17 (is it deprecated? cppreference states that it is)
// so we just provide the necessary typedefs in our iterator class
using iterator_category = std::forward_iterator_tag ;
using value_type = T ;
using pointer = T* ;
using reference = T& ;
using difference_type = std::ptrdiff_t ;
Iterator<T>( T* hook = nullptr ) : m_hook{hook} {} // default constructible
// note: it is the iterator that is is const ie. *iter does not modify iter
// the returned reference may or may not be a reference to const
// depending on what T is. for Iterator<int>, the result is int&
// and for Iterator< const int > the result is const int&
T& operator*() const { return *m_hook; }
// idiomatic: operator-> returns the address of the result of operator*
// we need to maintain only operator*. operator-> keeps itself in sync
T* operator-> () const { return std::addressof(**this) ; }
Iterator& operator++() { ++m_hook; return *this ; } // prefix increment
// requirement: both ++iter and iter++ must be supported
// postfix increment is typically implemented in terms of prefix increment
// make a copy of the old value; call pthe prefix increment,
// and return the before-increment copy as a prvalue
// we need to maintain only operator++(). operator++(int) keeps itself in sync
Iterator operator++(int) { auto old_val{*this}; return old_val ; } // postfix increment
// requirement: both equality and inequality comparison must be defined
bool operator== ( const Iterator<T>& other ) const { return m_hook == other.m_hook; }
// operator!= can be implemented as the negation of operator==
// we need to maintain only operator==. operator!= keeps itself in sync
bool operator!= ( const Iterator<T>& other ) const { return !( *this == other ); }
};
template < typename T, std::size_t N > Iterator<T> my_begin( T(&arr)[N] ) { return arr ; }
template < typename T, std::size_t N > Iterator<T> my_end( T(&arr)[N] ) { return arr+N ; }
int main()
{
struct My_class
{
int id ;
My_class( int id = 0 ) : id(id) {}
std::string speak() const { return "MyClass{" + std::to_string(id) + '}' ; }
};
const My_class arr[] { 0, 1, 2, 3, 4, 5, 6, 7 } ;
for( auto iter = my_begin(arr) ; iter != my_end(arr) ; ++iter )
std::cout << iter->speak() << '\n' ;
}
|