A custom iterator on a std::vector returns an object of the following class:
class chunk {
public:
int value_;
size_t fake_index_;
};
The iterator updates both the fake_index_ and value_.
After that I create a range class with the iterator. Then in a range base for loop over the range class, I only use the value_, but not fake_index_.
Does that means the compiler will optimize out the iterator's update of fake_index_ ?
Test Code (with all the iterator stuff...)
#include <vector>
#include <iterator>
class Iterator;
class Chunk {
public:
int value_;
int base_bit_index_;
int real_index_;
std::vector<int>& data_;
Chunk (std::vector<int>& data, size_t real_index)
: data_{data}
{
base_bit_index_ = real_index*sizeof(int);
real_index_ = real_index;
value_ = data[real_index_];
}
int value() { return value_; }
private:
void increment_index() {
++real_index_;
base_bit_index_ += sizeof(int);
value_ = data_[real_index_];
}
friend class Iterator;
};
class Iterator : public std::iterator<std::input_iterator_tag, int>
{
private:
Chunk chunk_;
public:
Iterator(Chunk chunk) :chunk_(chunk) {}
Iterator(const Iterator& mit) : chunk_(mit.chunk_) {}
Iterator& operator++() {
chunk_.increment_index();
return *this;
}
Iterator operator++(int) {Iterator tmp(*this); operator++(); return tmp;}
bool operator==(const Iterator& rhs)
{
return chunk_.real_index_== rhs.chunk_.real_index_;
}
bool operator!=(const Iterator& rhs)
{
return !(operator==(rhs));
}
Chunk& operator*() {return chunk_;}
};
class Range
{
private:
std::vector<int>& data_;
public:
Range(std::vector<int>& data)
: data_{data}
{
}
Iterator begin()
{
Chunk chunk(data_, 0);
Iterator tmp(chunk);
return tmp;
}
Iterator end()
{
Chunk chunk(data_, data_.size());
Iterator tmp(chunk);
return tmp;
}
};
int main()
{
std::vector<int> v1;
v1.resize(100);
std::vector<int> v2;
Range X(v1);
for (auto chunk : X) {
v2.push_back(chunk.real_index_);
}
}
gcc godbolt
Test Code (without all the iterator stuff)
#include <vector>
#include <iostream>
int main()
{
std::vector<int> v2;
for (size_t i = 0; i < 100; ++i) {
v2.push_back(i);
// std::cout<<i<<"\n";
}
}
gcc godbolt
My first impression: Looks like the iterator sucks horribly anyway.
Related
Summary
I have a custom array class:
template<typename T, int SIZE>
class Array {
private:
T mArray[SIZE];
};
To enable support for std algorithms, with ranges, I want to create an iterator for this class. It would seem that std::contiguous_iterator would be the optimal choice since I can guarantee contiguous memory layout for the data. Following the iterator tutorial I should create a class inside this class. However, I should somehow be (quoted) "For example, instead of the std::forward_iterator_tag tag you would mark your iterator with the std::forward_iterator concept.".
I have a hard time figuring out what the syntax would look like for this, and I have been unable to find a post on the web showcasing this.
Question
How do I complete the following code snippet to implement std::contiguous_iterator for my Array<T,S> class?:
import <iterator>;
template<typename T, int SIZE>
class Array {
public:
const T& operator[](int i) { return mArray[i]; }
T& operator[](int i) { return mArray[i]; }
private:
T mArray[SIZE];
public:
struct Iterator {
Iterator(T* ptr) : mPtr(ptr) {}
private:
T* mPtr;
};
Iterator begin() { return Iterator(&mArray[0]); }
Iterator end() { return Iterator(&mArray[SIZE]); }
};
NOTE: There is a lot of operator overloads. An answer is not required to provide all of them. I just need an example syntax for where to place the concept, then I can probably figure out the rest.
As far as I could tell you need typedefs on the iterator class, so simply using a pointer was not sufficient. Here is an example:
#include <iterator>
#include <algorithm>
template<typename T, int SIZE>
class Array {
public:
const T& operator[](int i) const { return mArray[i]; }
T& operator[](int i) { return mArray[i]; }
private:
T mArray[SIZE];
public:
struct iterator
{
using difference_type=std::ptrdiff_t;
using value_type=std::remove_cv_t<T>;
using pointer=T*;
using reference=T&;
using iterator_category=std::random_access_iterator_tag;
using iterator_concept=std::contiguous_iterator_tag;
using self_type=iterator;
iterator(T *x) : ptr(x) {}
T operator*() { return *ptr; }
T operator->() { return ptr; }
difference_type operator-(const iterator& rhs) { return ptr-rhs.ptr; }
iterator& operator ++() { ++ptr; return *this;}
bool operator !=(const iterator& rhs) { return ptr != rhs.ptr; }
private:
T * ptr;
};
iterator begin() { return &mArray[0]; }
iterator end() { return &mArray[SIZE]; }
};
int foo(Array<int, 7>& a)
{
int sum;
for (auto x : a)
{
sum+=x;
}
return sum;
}
int goo(Array<int, 7>& a, int x)
{
auto ret=std::find(a.begin(), a.end(), x);
if (ret!=a.end()) return *ret;
return 0;
}
Note that you would likely need const_iterator and reverse_iterators for const and non-const ...
Credits
Thanks to #glenn-teitelbaum for pointing me in the right direction. I think I managed to figure out how to do this. It took a long time, so this will hopefully save someone else that trouble.
[Answering my own question]
The iterator should comply with the std::contiguous_iterator concept, so I looked at cppreference for the necessary parts. The concept is defined like this:
template<class I>
concept contiguous_iterator =
std::random_access_iterator<I> &&
std::derived_from</*ITER_CONCEPT*/<I>, std::contiguous_iterator_tag> &&
std::is_lvalue_reference_v<std::iter_reference_t<I>> &&
std::same_as<
std::iter_value_t<I>, std::remove_cvref_t<std::iter_reference_t<I>>
> &&
requires(const I& i) {
{ std::to_address(i) } ->
std::same_as<std::add_pointer_t<std::iter_reference_t<I>>>;
};
So in order to implement this concept, I must first also implement std::random_access_iterator, std::is_derived_from<[...]>, std::is_lvalue_reference_v<[...]>, and so on. This is a recursive process, especially since std::random_access_iterator builds on top of 4 other iterator types. Since this is a C++20 question, I aim to use C++20 features as much as possible (since it greatly simplifies the implementation). This is the complete iterator implementation:
template<typename T, int SIZE>
class Array
{
public:
class Iterator
{
public:
using iterator_category = std::contiguous_iterator_tag;
using iterator_concept = std::contiguous_iterator_tag;
//using difference_type = std::ptrdiff_t; // Likely the same
using difference_type = typename std::iterator<
std::contiguous_iterator_tag, T>::difference_type;
//using value_type = T;
using value_type = std::remove_cv_t<T>; // Using `T` seems sufficient
using pointer = T*;
using reference = T&;
// constructor for Array<T,S>::begin() and Array<T,S>::end()
Iterator(pointer ptr) : mPtr(ptr) {}
// std::weakly_incrementable<I>
Iterator& operator++() { ++mPtr; return *this; }
Iterator operator++(int) { Iterator tmp = *this; ++(*this); return tmp; }
Iterator() : mPtr(nullptr/*&mArray[0]*/) {} // TODO: Unsure which is correct!
// std::input_or_output_iterator<I>
reference operator*() { return *mPtr; }
// std::indirectly_readable<I>
friend reference operator*(const Iterator& it) { return *(it.mPtr); }
// std::input_iterator<I>
// No actions were needed here!
// std::forward_iterator<I>
// In C++20, 'operator==' implies 'operator!='
bool operator==(const Iterator& it) const { return mPtr == it.mPtr; }
// std::bidirectional_iterator<I>
Iterator& operator--() { --mPtr; return *this; }
Iterator operator--(int) { Iterator tmp = *this; --(*this); return tmp; }
// std::random_access_iterator<I>
// std::totally_ordered<I>
std::weak_ordering operator<=>(const Iterator& it) const {
return std::compare_three_way{}(mPtr, it.mPtr);
// alternatively: `return mPtr <=> it.mPtr;`
}
// std::sized_sentinel_for<I, I>
difference_type operator-(const Iterator& it) const { return mPtr - it.mPtr; }
// std::iter_difference<I> operators
Iterator& operator+=(difference_type diff) { mPtr += diff; return *this; }
Iterator& operator-=(difference_type diff) { mPtr -= diff; return *this; }
Iterator operator+(difference_type diff) const { return Iterator(mPtr + diff); }
Iterator operator-(difference_type diff) const { return Iterator(mPtr - diff); }
friend Iterator operator+(difference_type diff, const Iterator& it) {
return it + diff;
}
friend Iterator operator-(difference_type diff, const Iterator& it) {
return it - diff;
}
reference operator[](difference_type diff) const { return mPtr[diff]; }
// std::contiguous_iterator<I>
pointer operator->() const { return mPtr; }
using element_type = T;
private:
T* mPtr;
};
// === STATIC ASSERTS ===
// - to verify correct Iterator implementation!
static_assert(std::weakly_incrementable<Iterator>);
static_assert(std::input_or_output_iterator<Iterator>);
static_assert(std::indirectly_readable<Iterator>);
static_assert(std::input_iterator<Iterator>);
static_assert(std::incrementable<Iterator>);
static_assert(std::forward_iterator<Iterator>);
static_assert(std::bidirectional_iterator<Iterator>);
static_assert(std::totally_ordered<Iterator>);
static_assert(std::sized_sentinel_for<Iterator, Iterator>);
static_assert(std::random_access_iterator<Iterator>);
static_assert(std::is_lvalue_reference_v<std::iter_reference_t<Iterator>>);
static_assert(std::same_as<std::iter_value_t<Iterator>,
std::remove_cvref_t<std::iter_reference_t<Iterator>>>);
static_assert(std::contiguous_iterator<Iterator>);
const T& operator[](int i) const {
if (i < 0 || i >= SIZE) {
throw std::runtime_error("Array index out of bounds");
}
return mArray[i];
}
T& operator[](int i) {
if (i < 0 || i >= SIZE) {
throw std::runtime_error("Array index out of bounds");
}
return mArray[i];
}
Iterator begin() { return Iterator(&mArray[0]); }
Iterator end() { return Iterator(&mArray[SIZE]); }
private:
T mArray[SIZE];
};
// Check that the Array class can be used as a contiguous_range.
static_assert(std::ranges::contiguous_range<Array<int, 10>>);
NOTE: using element_type = T; was necessary because of a bug in the specification, which might be fixed. I found information about that here. Adding this fixed issue with std::to_address<Iterator> not being able to compile, and was the last missing piece in going from std::random_access_iterator to std::contiguous_iterator.
Testing
I did not perform a complete testing suite with all algorithms, but I chose a few which depend on ranges and std::random_access_iterator. It all runs smoothly. I also depend on building standard library headers as module units, because I want to showcase how C++20 features work together.
import <stdexcept>;
import <iostream>;
import <iterator>;
import <algorithm>;
import <random>;
#include <memory> // fails to build header unit!
template<typename T, int SIZE>
class Array
{
[...]
};
int main()
{
Array<int, 10> arr;
for (int i = 0; i < 10; i++) arr[i] = i;
// I need to call std::ragnes::shuffle since that depends on
// std::random_access_iterator, so that is a minimum.
// https://en.cppreference.com/w/cpp/algorithm/ranges/shuffle
std::random_device rd;
std::mt19937 gen{rd()};
std::cout << "before random shuffle:\n";
for (auto& i : arr) std::cout << i << ' ';
std::ranges::shuffle(arr, gen);
std::cout << "\nafter random shuffle:\n";
for (auto& i : arr) std::cout << i << ' ';
std::cout << '\n';
// Also std::ranges::stable_sort is a good check (also random_access_iterator):
// https://en.cppreference.com/w/cpp/algorithm/ranges/stable_sort
std::cout << "after stable_sort:\n";
std::ranges::stable_sort(arr);
for (auto& i : arr) std::cout << i << ' ';
std::cout << '\n';
auto [min,max] = std::ranges::minmax(arr);
std::cout << "min: " << min << ", max: " << max << '\n';
return 0;
}
I have created a class called ring it works like that:
ring<std::string> example(2);
examples.add("one");
examples.add("two");
examples.add("three"); // this element will replace the first element
also, I have tried to make it iterable but it crashes with an exception Access violation reading location
the exception location is at cout in the main for loop
the program works very well without the for loop
the ring class:
template <class input>
class ring {
public:
class iterator;
private:
input* data;
int size;
int pos = 0;
public:
ring(const int size) : size(size), data(NULL) { data = new input[size]; }
~ring() { delete[] data; }
void add(input in) {
if (pos == size)
pos = 0;
data[pos++] = in;
}
iterator begin() { return iterator(0, *this); }
iterator end() { return iterator(size - 1, *this); }
input& get(int i) { return data[i]; }
};
the iterator class:
template <class input>
class ring<input>::iterator {
private:
int m_pos;
ring m_ring;
public:
iterator(int pos, ring& aRing) : m_pos(pos), m_ring(aRing) {}
~iterator() {}
input& operator* () { return m_ring.get(m_pos); }
bool operator!= (const iterator& other) const { return m_pos != other.m_pos; }
iterator& operator++(int) {
m_pos++;
return *this;
}
iterator& operator++() {
m_pos++;
return *this;
}
};
main:
int main() {
ring<string> textring(3);
textring.add("one");
textring.add("two");
textring.add("three");
textring.add("four");
for (auto text : textring) {
cout << text << endl;
}
}
Your program crashes because class ring doesn't implement rule of three. You should provide all copy operations.
What is the reason of crash?
In destructor the same memory is deleted twice due to shallow copy. This copy is made by begin/end which returns iterator in which you make copy of ring.
Quick solution, is to store ring inside iterator as reference:
template <class input>
class ring<input>::iterator {
private:
int m_pos;
ring& m_ring;
Demo
I've got this class:
template<typename T>
class Konten
{
enum { ssize = 100 };
T stack[ssize];
int top;
public:
Konten() : top(0) {}
void push(T i) {
assert(top < ssize); stack[top++] = i;
};
T pop() {
assert(top > 0); return stack[--top];
};
int rozmiar() { return top; };
class iterator {
Konten& s;
int index;
public:
iterator(Konten& is) : s(is), index(0) {};
iterator(Konten& is, bool) : s(is), index(s.top) {};
T operator++() { // Prefix
assert(index < s.top);
return s.stack[++index];
};
T operator++(int) { // Postfix
assert(index < s.top);
return s.stack[index++];
};
T& operator*() const { return s.stack[index]; };
iterator& operator=(const iterator& rv) {
s = rv.s;
index = rv.index;
return *this;
}
};
iterator begin() { return iterator(*this); };
iterator end() { return iterator(*this, true); };
friend class iterator;
};
And as you see it has another class inside it. I want to create an object of the iterator class this way:
Konten<double> pier;
iterator it1(pier);
But I keep on getting following error: "argument list for class template "iterator" is missing".
What am I doing wrong?
Your iterator needs to be declared as Konten<double>::iterator.
I implemented a trivial class MyClass that has an array allocated with new inside it (I know that I could use a STL container, but I'm trying to understand how they work). I also created an iterator subclass, able to iterate on all the elements of a MyClass object:
class MyIterator : public iterator<forward_iterator_tag,int>
{
private:
int* data= nullptr;
int length;
int pointer=0;
int nullvalue=0;
public:
MyIterator(int* data, int length, bool end= false)
{
this->data= data;
this->length= length;
if(end)
pointer=-1;
}
~MyIterator()
{
delete[] data;
}
MyIterator& operator++()
{
if(pointer!= length-1)
{
pointer++;
}
else
{
pointer= -1;
}
return *this;
}
bool operator==(const MyIterator& other)
{
return pointer==other.pointer;
}
bool operator!=(const MyIterator& other)
{
return pointer!= other.pointer;
}
int& operator* ()
{
if(pointer==-1)
return nullvalue;
else
return data[pointer];
}
};
class MyClass
{
private:
int* data= nullptr;
int length= 100;
public:
MyClass()
{
data= new int[length];
for(int i=0; i<length;i++)
data[i]=i+1;
}
iterator<forward_iterator_tag,int> begin()
{
return MyIterator(data,length);
}
iterator<forward_iterator_tag,int> end()
{
return MyIterator(data,length,true);
}
};
While the iterator works if I use it this way:
for(MyIterator i= MyClass_instance.begin(); i!=MyClass_instance.end();i++) {...}
It doesn't work if I try to use it with BOOST_FOREACH:
BOOST_FOREACH(int i, MyClass_instance) {...}
These are the errors that I get:
You're slicing your iterator by returning them as std::iterator<> by value. You cannot do that.
Returning by reference would avoid the slicing problem but introduces a worse problem: it returns a reference to a temporary¹.
Hence, fix it by returning your actual iterator type by value.
Your type was missing a const iterator.
All the iterator members weren't const-correct.
Also, according to the page Extensibility it looks like you need to add
namespace boost {
template<> struct range_mutable_iterator<MyClass> {
typedef MyClass::MyIterator type;
};
template<> struct range_const_iterator<MyClass> {
typedef MyClass::MyConstIterator type;
};
}
There is serious trouble with the Rule-Of-Three implementation for your iterator type (What is The Rule of Three?).
You're deleting the container's data every time an iterator goes out of existence. And MyClass itself never frees the data...
Fixing most (?) of the above:
Live On Coliru
#include <iterator>
#include <boost/foreach.hpp>
class MyClass
{
private:
int* data = nullptr;
int length = 100;
public:
class MyIterator : public std::iterator<std::forward_iterator_tag, int>
{
public:
MyIterator(int* data, int length, bool end = false)
{
this->data= data;
this->length= length;
if(end)
pointer=-1;
}
MyIterator& operator++()
{
if(pointer!= length-1) {
pointer++;
}
else {
pointer= -1;
}
return *this;
}
bool operator==(const MyIterator& other) const { return pointer==other.pointer; }
bool operator!=(const MyIterator& other) const { return pointer!= other.pointer; }
int& operator*() const
{
if(pointer==-1)
return nullvalue;
else
return data[pointer];
}
private:
value_type* data = nullptr;
int length;
int pointer = 0;
mutable value_type nullvalue = 0;
};
class MyConstIterator : public std::iterator<std::forward_iterator_tag, const int>
{
public:
MyConstIterator(int const* data, int length, bool end = false)
{
this->data= data;
this->length= length;
if(end)
pointer=-1;
}
MyConstIterator& operator++()
{
if(pointer!= length-1) {
pointer++;
}
else {
pointer= -1;
}
return *this;
}
bool operator==(const MyConstIterator& other) const { return pointer==other.pointer; }
bool operator!=(const MyConstIterator& other) const { return pointer!= other.pointer; }
int const& operator*() const
{
if(pointer==-1)
return nullvalue;
else
return data[pointer];
}
private:
value_type* data = nullptr;
int length;
int pointer = 0;
value_type nullvalue = 0;
};
public:
typedef MyIterator iterator_type;
typedef MyConstIterator const_iterator_type;
MyClass()
{
data= new int[length];
for(int i=0; i<length;i++)
data[i]=i+1;
}
~MyClass() {
delete[] data;
}
iterator_type begin() { return MyIterator(data,length); }
iterator_type end() { return MyIterator(data,length,true); }
const_iterator_type begin() const { return MyConstIterator(data,length); }
const_iterator_type end() const { return MyConstIterator(data,length,true); }
};
namespace boost {
template<> struct range_mutable_iterator<MyClass> {
typedef MyClass::MyIterator type;
};
template<> struct range_const_iterator<MyClass> {
typedef MyClass::MyConstIterator type;
};
}
#include <iostream>
int main()
{
MyClass c;
BOOST_FOREACH(int i, c) {
std::cout << i << "\n";
}
}
¹ (unless you store the iterators somewhere else, but that would be a huge anti-pattern for many many reasons)
I use an API that comes with an iteration functionality using a void* handle.
void* handle = BrowseInit();
while (BrowseGetNext(handle))
{
// ...
int x = BrowseGetData(handle);
}
BrowseFree(handle);
How would I go about wrapping this into a C++11 iterator for use in range-based for-loops? Since the value of the handle doesn't actually change, I need some trickery in operator != ().
class Iterator
{
public:
friend class it;
class it
{
public:
it(Iterator* data) : _data(data) { }
bool operator != (const it& other) const
{
// what should I put in here?
}
const it& operator ++ ()
{
BrowseGetNext(_data->_handle);
}
int operator * () const
{
return BrowseGetData(_data->_handle);
}
private:
Iterator* _data;
};
Iterator() : _handle(BrowseInit()) { }
~Iterator()
{
BrowseFree(_handle);
}
it begin() const
{
return it(this);
}
it end() const
{
return it(nullptr);
}
private:
void* _handle;
};
This should work.
class Iterator
{
public:
friend class it;
class it
{
public:
// Constructor for begin().
it(Iterator* data) : _data(data), index_(0) { }
// Constructor for end().
it(Iterator* data, int) : _data(data), index_(-1) { }
bool operator != (const it& other) const
{
return !(*this == other);
}
bool operator == (const it& other) const
{
return ( this->_data == other._data && this->_index == rhs._index );
}
const it& operator ++ ()
{
// Increment the index if there's more data.
// Otherwise, set it to -1.
if ( BrowseGetNext(_data->_handle) )
{
++index_;
}
else
{
index_ = -1;
}
}
int operator * () const
{
return BrowseGetData(_data->_handle);
}
private:
Iterator* _data;
int _index;
};
Iterator() : _handle(BrowseInit()) { }
~Iterator()
{
BrowseFree(_handle);
}
it begin() const
{
return it(this);
}
it end() const
{
return it(this, 0);
}
private:
void* _handle;
};
Update: Setup iterator_traits for it
template <> struct std::iterator_traits<Iterator::it>
{
typedef int difference_type;
typedef int value_type;
typedef int* pointer;
typedef int& reference;
typedef std::forward_iterator_tag iterator_category;
};
Thanks for the suggestion, Yakk.
Update 2
Instead of specializing std::iterator for Iterator::it, derive Iterator::it from std::iterator.
class it : public std::iterator<std::forward_iterator_tag, int, int>
{
....
};