C++20 : Making reverse iterator from custom iterator fails - c++

I'm making a reverse iterator from my custom iterator on my B-Tree implementation, but it fails to work.
Full code : https://wandbox.org/permlink/hrM32RYb6oVHDHAm
My iterator implementation:
class BTreeIterator {
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
using iterator_category = std::bidirectional_iterator_tag;
Node* node;
std::vector<T>::iterator it;
void Increment() {
// ...
}
void Decrement() {
// ...
}
public:
BTreeIterator(Node* node, std::size_t i) : node {node} {
assert(node && i <= node->key.size());
it = node->key.begin() + i;
}
reference operator*() const {
return *it;
}
pointer operator->() const {
return it;
}
BTreeIterator& operator++() {
Increment();
return *this;
}
BTreeIterator operator++(int) {
BTreeIterator temp = *this;
Increment();
return temp;
}
BTreeIterator& operator--() {
Decrement();
return *this;
}
BTreeIterator operator--(int) {
BTreeIterator temp = *this;
Decrement();
return temp;
}
friend bool operator==(const BTreeIterator& x, const BTreeIterator& y) {
return x.node == y.node && x.it == y.it;
}
friend bool operator!=(const BTreeIterator& x, const BTreeIterator& y) {
return !(x == y);
}
};
My typedef declaration in class BTree:
using iterator = BTreeIterator;
using const_iterator = BTreeConstIterator;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
iterator begin_;
const_iterator cbegin_;
iterator end_;
const_iterator cend_;
// ...
reverse_iterator rbegin() {
return reverse_iterator(end_);
}
const_reverse_iterator rbegin() const {
return const_reverse_iterator(cend_);
}
const_reverse_iterator crbegin() const {
return const_reverse_iterator(cend_);
}
reverse_iterator rend() {
return reverse_iterator(begin_);
}
const_reverse_iterator rend() const {
return const_reverse_iterator(cbegin_);
}
const_reverse_iterator crend() const {
return const_reverse_iterator(cbegin_);
}
The compiler complains for this reason:
error: return-statement with a value, in function returning 'std::reverse_iterator<BTree<int, 2>::BTreeConstIterator>::reference' {aka 'void'}
The standard gives (https://en.cppreference.com/w/cpp/iterator/reverse_iterator):
Until C++20, std::reverse_iterator<Iter>::reference had been equal to std::iterator_traits<Iter>::reference (In my case, T&)
Since C++20, std::reverse_iterator<Iter>::reference has been changed to
std::iter_reference_t<Iter> = decltype(*std::declval<Iter&>())
It seems to became void in my case, causing the error.
My questions:
What is the rationale behind changing a reference type of a derived reverse iterator?
Why my BTreeIterator type is not referenceable? decltype(*std::declval<BTreeIterator&>()) gives void. How can I change it to T&?

Related

Custom iterator for the Matrix class

I wrote my own matrix class with fields like this
template <typename T>
class Matrix
{
private:
T *data = nullptr;
size_t rows;
size_t cols;
....
I tried to make an iterator for my matrix, but it didn't work. An error like this is thrown in method Iterator begin(and end):
error: cannot convert ‘myMatrix::Matrix<int>::Iterator’ to ‘int*’ in return
The iterator must support STL functions such as std::find_if(). How can I fix the iterator so that it works correctly?
class Iterator
{
friend Matrix;
private:
T *curr;
public:
using iterator_category = std::input_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type &;
Iterator() : curr(nullptr) {}
Iterator(T *other) : curr(other) {}
~Iterator() = default;
bool operator==(const Iterator &it) const { return curr == it.curr; }
bool operator!=(const Iterator &it) const { return !(curr == it.curr); }
Iterator &operator++()
{
++curr;
return *this;
}
Iterator operator++(int)
{
Iterator temp = *this;
operator++();
return temp;
}
Iterator &operator+(int n)
{
for (int i = 0; i < n; i++)
{
++(*this);
}
return *this;
}
T &operator*() const { return *curr; }
T *operator->() const { return curr; }
};
Iterator begin()
{
Iterator it(data);
return it;
}
Iterator end()
{
Iterator it(data + rows * cols);
return it;
}

C++ Custom Vector Class - bounds checking iterator for incrementation

I made my custom vector class with a nested iterator class. Now I want to make sure the increment ++ method of the iterator class, can not be used to go beyond the one element after the last element of the vector . How can I do that?
Thank you!
Edit: here my nested iterator class (I left out vector class, ConstIterator).
class Iterator {
public:
using value_type = Vector::value_type;
using reference = Vector::reference;
using pointer = Vector::pointer;
using difference_type = Vector::difference_type;
using iterator_category = forward_iterator_tag;
private:
pointer ptr;
public:
Iterator():ptr{nullptr}{}
Iterator(pointer point): ptr{point}{}
reference operator*() const{ //if (ptr == .end()) throw runtime_error
return *ptr;
}
pointer operator->() const{
return ptr;
}
bool operator==(const const_iterator& c_it) const{
if(ptr == c_it.get_pointer())
return true;
else
return false;
}
bool operator!=(const const_iterator& c_it) const{
if(ptr == c_it.get_pointer())
return false;
else
return true;
}
iterator& operator++(){
++ptr;
return *this;
}
iterator operator++(int){
pointer copy = ptr;
++ptr;
return copy;
}
operator const_iterator() const{
return const_iterator(ptr);
}
};

Implement custom Iterator and const_iterator for doubly linked list in c++

Here is my implementation of iterator and const_iterator for Doubly Linked List. Program has mandatory tests. When I am running my program I get error from tests:
"Decrement operator moves the iterator backward, Prefix", due to a
fatal error condition: SIGSEGV - Segmentation violation signal
What am I doing wrong?
test.cpp // here is my test file where I get error //
TEST_CASE("Decrement operator moves the iterator backward", "[stage2]") {
list l;
append_to_list(l, { 5.55, 6.66, 7.77, 8.88 });
auto it = l.end();
SECTION("Prefix") {
REQUIRE(*(--it) == 8.88);
REQUIRE(*(--it) == 7.77);
REQUIRE(*(--it) == 6.66);
REQUIRE(*(--it) == 5.55);
REQUIRE(it == l.begin());
}
SECTION("Postfix") {
it--;
REQUIRE(*(it--) == 8.88);
REQUIRE(*(it--) == 7.77);
REQUIRE(*(it--) == 6.66);
REQUIRE(*it == 5.55);
REQUIRE(it == l.begin());
}
}
TEST_CASE("Decrement operator moves the (const) iterator backward", "[stage2]") {
list l;
append_to_list(l, { 5.55, 6.66, 7.77, 8.88 });
auto it = l.cend();
SECTION("Prefix") {
REQUIRE(*(--it) == 8.88);
REQUIRE(*(--it) == 7.77);
REQUIRE(*(--it) == 6.66);
REQUIRE(*(--it) == 5.55);
REQUIRE(it == l.cbegin());
}
SECTION("Postfix") {
it--;
REQUIRE(*(it--) == 8.88);
REQUIRE(*(it--) == 7.77);
REQUIRE(*(it--) == 6.66);
REQUIRE(*it == 5.55);
REQUIRE(it == l.cbegin());
}
}
list.hpp
class list {
private:
struct node {
double val = 0;
node* prev = nullptr;
node* next = nullptr;
};
node* head = nullptr;
node* tail = nullptr;
size_t num_elements = 0;
public:
class const_iterator {
node* current_ptr = nullptr;
const list* o_list = nullptr;
public:
using difference_type = std::ptrdiff_t;
using iterator_category = std::bidirectional_iterator_tag;
using value_type = const double;
using reference = const double&;
using pointer = const double*;
const_iterator() = default;
const_iterator(node* ptr, const list* gen);
const_iterator& operator++();
const_iterator operator++(int);
const_iterator& operator--();
const_iterator operator--(int);
reference operator*() const;
pointer operator->() const;
bool operator==(const const_iterator& rhs) const;
bool operator!=(const const_iterator& rhs) const;
friend class list;
};
class iterator {
node* current_ptr = nullptr;
const list* o_list = nullptr;
public:
using difference_type = std::ptrdiff_t;
using iterator_category = std::bidirectional_iterator_tag;
using value_type = double;
using reference = double&;
using pointer = double*;
iterator() = default;
iterator(node* ptr, const list* gen);
iterator& operator++();
iterator operator++(int);
iterator& operator--();
iterator operator--(int);
reference operator*() const;
pointer operator->() const;
operator const_iterator() const;
bool operator==(const iterator& rhs) const;
bool operator!=(const iterator& rhs) const;
friend class list;
};
list.cpp // my implementation of iterator and const_iterator //
list::iterator list::begin() {
return list::iterator(head, this);
}
list::iterator list::end() {
return list::iterator(tail->next, this);
}
list::const_iterator list::begin() const {
return list::const_iterator(head, this);
}
list::const_iterator list::end() const {
return list::const_iterator(tail->next, this);
}
list::const_iterator list::cbegin() const {
return list::const_iterator(head, this);
}
list::const_iterator list::cend() const {
return list::const_iterator(tail->next, this);
}
list::iterator::iterator(node *ptr, const list *gen) {
this->current_ptr = ptr;
this->o_list = gen;
}
list::iterator& list::iterator::operator++() {
current_ptr = this->current_ptr->next;
return *this;
}
list::iterator& list::iterator::operator--() { //here triggers error!!!
current_ptr = this->current_ptr->prev;
return *this;
}
list::iterator list::iterator::operator++(int)
{
iterator old(*this);
++(*this);
return old;
}
list::iterator list::iterator::operator--(int)
{
iterator left(*this);
--(*this);
return left;
}
list::const_iterator& list::const_iterator::operator++() {
current_ptr = current_ptr->next;
return *this;
}
list::const_iterator& list::const_iterator::operator--() { //here triggers error!!!
current_ptr = current_ptr->prev;
return *this;
}
list::const_iterator list::const_iterator::operator++(int) {
const_iterator old = *this;
++(*this);
return old;
}
list::const_iterator list::const_iterator::operator--(int) {
const_iterator old = *(this);
--(*this);
return old;
}
The problem is that you are using a null node * as your end(), which fails to work with operator--; there's no way to decrement it to get at the last node of the list.
You need to use something else as your end sentinel which allows you to get back to the list -- either a fake 'node' on the list that isn't an element of the real list, or add a pointer to the original list to the iterator classes, so when you decrement the end() iterator you can recover the last node in the list.
Since your iterator already has an o_list pointer, it's easiest just to use that:
list::iterator& list::iterator::operator--() {
current_ptr = current_ptr ? current_ptr->prev : o_list->tail;
return *this;
}

How to implement function without argument that return size of Doubly Linked list? int size() const

I need to implement following function:
int size() const;
function returns number of pieces of data stored in the list
Time complexity - O(1).
Basically I have a class called DList contained in DList.h which has the following structure:
class DList
{
struct Node
{
T data_;
Node *next_;
Node *prev_;
Node(const T &data = T{}, Node *next = nullptr, Node *prev = nullptr)
{
data_ = data;
next_ = next;
prev_ = prev;
}
};
Node *front_;
Node *back_;
public:
class const_iterator
{
friend class DList;
protected:
Node *curr_;
//assign curr_ with n;
const_iterator(Node *n)
{
curr_ = n;
}
public:
const_iterator()
{
curr_ = nullptr;
}
const_iterator operator++()
{
//++x
curr_ = curr_->next_;
return *this;
};
const_iterator operator++(int)
{
//x++
const_iterator old = *this;
curr_ = curr_->next_;
return old;
};
const_iterator operator--()
{
curr_ = curr_->prev_;
return *this;
}
const_iterator operator--(int)
{
//x--
const_iterator old = *this;
curr_ = curr_->prev_;
return old;
}
bool operator==(const_iterator rhs)
{
return (curr_ == rhs.curr_);
}
bool operator!=(const_iterator rhs)
{
return (curr_ != rhs.curr_);
}
const T &operator*() const
{
return curr_->data_;
}
};
class iterator : public const_iterator
{
friend class DList;
iterator(Node *n) : const_iterator(n) {}
public:
iterator() {}
iterator operator++()
{
//++x
this->curr_ = this->curr_->next_;
return *this;
}
iterator operator++(int)
{
//x++
iterator old = *this;
this->curr_ = this->curr_->next_;
return old;
}
iterator operator--()
{
//--x
this->curr_ = this->curr_->prev_;
return *this;
}
iterator operator--(int)
{
//x--
iterator old = *this;
this->curr_ = this->curr_->prev_;
return old;
}
T &operator*()
{
return this->curr_->data_;
}
};
DList();
~DList();
DList(const DList &rhs);
DList &operator=(const DList &rhs);
DList(DList &&rhs);
DList &operator=(DList &&rhs);
iterator begin()
{
return iterator(front_);
}
iterator end()
{
return iterator(nullptr);
}
const_iterator begin() const
{
return const_iterator(front_);
}
const_iterator end() const
{
return const_iterator(nullptr);
}
void push_front(const T &data);
void push_back(const T &data);
void pop_front();
void pop_back();
iterator insert(const T &data, iterator it);
iterator search(const T &data);
const_iterator search(const T &data) const;
iterator erase(iterator it);
iterator erase(iterator first, iterator last);
bool empty() const;
int size() const; <--------- THIS IS THE FUNCTION I NEED TO IMPLEMENT
};
The function size() is being called in the tester program main.cpp as following:
DList<Record> recList;
DList<int> intList;
.
.
.
.
if (!checkList(recList, mirror, 15) || recList.empty() || recList.size() != 15)
{
passtest = false;
cout << "Error 9a: Bug in = operator, no deepcopy?" << endl;
}
For O(1) complexity, you need to keep a running count that is updated whenever you add or remove a node. The default constructor must initialize it to zero, and other constructors and assignments must do the right thing (DTRT).
class DList
{
struct Node;
Node *front_;
Node *back_;
size_t count_;
public:
Dlist()
: front_(nullptr)
, back_(nullptr)
, count_(0)
{}
size_t size() const { return count_; }
// etc..
}

Implementation of map - how to differ two ends of the map in iterator

Iterator Implementation:
class iterator {
private:
Node<Pair>* _ptr = nullptr;
public:
iterator(Node<Pair>* ptr = nullptr) : _ptr(ptr) { }
iterator(const iterator& itr) = default;
~iterator() = default;
iterator& operator=(const iterator&) = default;
iterator operator++(int) {
Node<Pair> *cur = this->_ptr;
if (this->_ptr) {
this->_ptr = _ptr->Next();
}
return cur;
}
iterator& operator++() {
if (this->_ptr) {
this->_ptr = _ptr->Next();
}
return *this;
}
Pair& operator*() {
if (!this->_ptr) {
throw MapElementNotFoundException();
}
return this->_ptr->Data();
}
bool operator==(const iterator& itr) const {
if ((this->_ptr == nullptr && itr._ptr == nullptr) ||
(this->_ptr == itr._ptr)) {
return true;
}
return false;
}
friend bool operator!=(const iterator& itr1, const iterator& itr2) {///// non member
return !(itr1 == itr2);
}
};
Private of Map class:
Node<Pair> *_head;
ValueType _default; /////////////////////////////////////////////////
int _size;
The methods begin and end:
iterator begin() const{
return iterator(this->_head);
}
iterator end() const{
return iterator(nullptr); //////////////////////////////////////////////
}
How can I implement the iterator and end method so this would work:
ASSERT_EQUALS(false, map9.end() == map7.end());
My goal is to implement the iterator in such way, in which I could implement correctly the operator == and != for iterators, as two iterators are identical if point to the same object in the same map, and different in other way.
Edit: (Solution)
private:
Node<Pair>* _ptr/* = nullptr */;
MtmMap<ValueType, KeyType, CompareFunction>* _map_ptr;
public:
iterator(Node<Pair>* ptr/* = nullptr*/,const MtmMap<ValueType, KeyType, CompareFunction> * mtm_ptr)
: _ptr(ptr), _map_ptr(mtm_ptr){ }
Why the compiler says that there is here a const-correctnes problem. It say that it can't cast from const MtmMap * mtm_ptr to MtmMap * mtm_ptr.
But it just copy of the ptr.