I am trying to create linked list class and define the iterators, I have all of them except the last one. I dont get how to fix, i get this error when i compile my code:
no match for ‘operator!=’ in ‘recList.SortedList::begin with T = Record != recList.SortedList::end with T = Record’
a1q1main.cpp:114:37: note: candidates are:
sortedlist.h:112:11: note: SortedList::iterator SortedList::iterator::operator!=(bool) [with T = Record, SortedList::iterator = SortedList::iterator]
sortedlist.h:112:11: note: no known conversion for argument 1 from ‘SortedList::iterator’ to ‘bool’
It keeps on displaying this error no matter what i do
I have declared operator == and everything is fine but the != complains
Here is the code :
class SortedList {
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* head_;
Node* tail_;
public:
class const_iterator {
protected:
Node* curr_;
public:
const_iterator(Node* p) {
curr_ = p;
}
.........
const_iterator operator--(int) {
const_iterator tmp = *this;
curr_ = curr_->prev_;
return tmp;
}
const T& operator*() const {
return curr_->data_;
}
const_iterator operator==(bool){
return false;
}
const_iterator operator!=(bool){
return true;
}
return;`
I need to fulfill the criteria below:
operator !=
returns true if two iterators point at different nodes, false otherwise
O(1)
I did not complete the logic of the operator, i just need to declare it properly so i dont get the error
The signature of your operator overloads are incorrect. You have them accepting a bool and returning an iterator. It should be the other way around.
Consider the operation you are performing
if(it1 == it2){
// do stuff
}
You even return a bool in your functions despite the signature requiring a iterator as return value.
Instead implement the operator overloads in the required signature
bool operator==(sorted_list_iterator it){
return (curr_->data_ == it.curr_->data_);
}
bool operator!=(sorted_list_iterator it){
return !(*this == it);
}
Note you can use your operator== overload in your operator!= to avoid repeating the equality logic in two functions. You may also be required to allow for a null curr_ in those functions.
Related
i implemented a custom List including an iterator using the example from a textbook, but when i try to iterate over the list using the iterator, i get the error:
Error C2675 unary '++': 'List::iterator' does not define this
operator or a conversion to a type acceptable to the predefined
operator
does anyone know what's wrong with my class? i'm pretty sure i copied it verbatim from the textbook. thanks!
/*
list class itself - contains links to both ends, size, and many other methods
concrete implementation of List ADT. different from vector because it allows
us to add to middle of list in O(1) time, but does not support O(1) indexing[]
*/
template <typename Object>
class List {
private:
/*
private nested Node class. contains data and pointers to prev/next
along with appropriate constructor methods
struct is a relic from C, essentially a class where members default to public
used to signify a type that contains mostly data to be accessed directly rather
than through methods
Node is private, so only List will be able to access it's fields/methods
*/
struct Node {
Object data;
Node* prev;
Node* next;
Node(const Object& d = Object{}, Node* p = nullptr, Node* n = nullptr) :
data{ d }, prev{ p }, next{ n } {}
Node(Object&& d, Node* p = nullptr, Node* n = nullptr) :
data{ std::move(d) }, prev{ p }, next{ n }
{}
};
public:
/*
const_iterator class. abstracts notion of position, public nested class
const_iterator stores pointer to current node and provides implementation of
basic iterator operations, all in form of overloaded operators like =, ==, !=, ++
*/
class const_iterator {
public:
const_iterator():current{nullptr}{}
const Object& operator*()const
{
return retrieve();
}
const_iterator& operator++()
{
current = current->next;
return *this;
}
const_iterator operator++(int)
{
const_iterator old = *this;
++(*this);
return old;
}
/*
public methods of const_iterator all use operator overloading. operator==,
operator!=, operator* are straightforward. separate routines are necessary for
prefix and postfix versions of operator++ due to their different semantics. we
distinguish between them by their method signatures (empty parameter for prefix,
int parameter for postfix). int is only used to distinguish between them.
in many cases where there is a choice between using prefix or postfix, prefix
version is faster.
*/
bool operator==(const const_iterator& rhs)const
{
return current == rhs.current;
}
bool operator!=(const const_iterator& rhs)const
{
return !(*this == rhs);
}
/*
protected allows classes that inherit from const_iterator to access these fields
but not other classes
*/
protected:
Node* current;
Object& retrieve()const
{
return current->data;
}
const_iterator(Node* p) : current{ p }
{
}
/*
the friend declaration is needed to grant the List class access to const_iterators
nonpublic members
*/
friend class List<Object>;
};
/*
iterator class. abstracts notion of position. public nested class
same functionality as const_iterator, except operator* returns a reference
to the item being viewed, rather than a const reference
iterator can be used in any routine that requires a const_iterator, but not
vice-versa (in other words, iterator IS A const_iterator)
iterator inherits from const_iterator, meaning it inherits all the data and
methods from const_iterator. it can then add new methods and override existing
methods. here we are not adding any new data or changing the behavior of exising
methods. we do add some new methods (with similar signatures to const_iterator)
*/
class iterator : public const_iterator
//inheritance: iterator has same functionality as const_iterator
//iterator can be used wherever const_iterator is needed
{
public:
iterator() {}
/*
do not have to re-implement operator == and operator != (inherited unchanged)
provide a new pair of operator++ implementations that override the original
implementations from const_iterator.
provide an accessor/mutator pair for operator*.
*/
Object& operator*()
{
return const_iterator::retrieve();
}
const Object& operator*()const
{
return const_iterator::operator*();
}
iterator& operator++(int)
{
iterator old = *this;
++(*this);
return old;
}
protected:
/*
protected constructor uses an initialization list to initialize the inherited
current node.
*/
iterator(Node* p) :const_iterator{ p } {}
friend class List<Object>;
};
public:
/*
constructor and big 5. because zero-parameter constructor and copy constructor
must both allocate the header and tail nodes, we provide a init routine.
init creates an empty list.
the destructor reclaims the header and tail nodes, all other nodes, all other
nodes are reclaimed when the destructor invokes clear. similarly, the
copy-constructor is implemented by invoking public methods rather than attempting
low-level pointer manipulations.
*/
List()
{
init();
}
/*
sentinel nodes - makes sense to create an extra node at each end of list to
represent start/end markers. also referred to as header and tail nodes
advantage of sentinel nodes is that it simplifies code by removing special cases
example: without sentinels, removing the first node becomes a special case
because we must reset the list's link to the first node during the remove
operation, and because the remove algorithm in general needs to access the node
prior to the node being removed (and without header, first node does not have
prior)
*/
void init()
{
theSize = 0;
head = new Node;
tail = new Node;
head->next = tail;
tail->prev = head;
}
~List()
{
clear();
delete head;
delete tail;
}
// clear works by repeatedly removing items until the List is empty (uses pop-front)
void clear()
{
while (!empty())
pop_front();
}
List(const List& rhs)
{
init();
for (auto& x : rhs)
{
push_back(x);
}
}
List& operator=(const List& rhs)
{
List copy = rhs;
std::swap(*this, copy);
return *this;
}
List(List&& rhs)
:theSize{ rhs.theSize }, head{ rhs.head }, tail{ rhs.tail }
{
rhs.theSize = 0;
rhs.head = nullptr;
rhs.tail = nullptr;
}
List& operator=(List&& rhs)
{
std::swap(theSize, rhs.theSize);
std::swap(head, rhs.head);
std::swap(tail, rhs.tail);
return *this;
}
// these methods return iterators
iterator begin()
{
return { head->next };
}
const_iterator begin()const
{
return{ head->next };
}
iterator end()
{
return { tail };
}
const_iterator end()const
{
return{ tail };
}
int size()const
{
return theSize;
}
bool empty()const
{
return size() == 0;
}
Object& front()
{
return *begin();
}
const Object& front()const
{
return *begin();
}
Object& back()
{
return *--end();
}
const Object& back() const
{
return *--end();
}
/*
cleverly obtain and use appropriate iterator
insert inserts prior to a position, so push_back inserts prior to the endmarker
pop_back line erase(-end()) creates a temporary iterator corresponding to the
endmarker, retreats the temporary iterator, and uses that iterator to erase.
similar behavior occurs in back
note also that we avoid dealing with node reclamation in pop_front and pop_back
*/
void push_front(const Object& x)
{
insert(begin(), x);
}
void push_front(Object&& x)
{
insert(begin(), std::move(x));
}
void push_back(const Object& x)
{
insert(end(), x);
}
void push_back(Object&& x)
{
insert(end(), std::move(x));
}
void pop_front()
{
erase(begin());
}
void pop_back()
{
erase(--end());
}
/*
inserting a new node between p and p->prev. works by getting a new node
and then changing pointers of p and p-prev in the correct order
also mention usefulness of the sentinels here.
*/
iterator insert(iterator itr, const Object& x)
{
Node* p = itr.current;
theSize++;
Node* newNode = new Node{ x, p->prev,p };
p->prev->next = newNode;
p->prev = newNode;
return newNode;
}
iterator insert(iterator itr, Object&& x)
{
Node* p = itr.current;
theSize++;
p->prev->next = new Node{ std::move(x), p->prev, p };
p->prev = p->prev->next;
return p->prev;
}
/*
erase routines. frst version erases p by rearranging pointers of the nodes just
before and after p, and then returning an iterator representing the item after the
erased element. like insert, erase must also update the size.
second version of erase simply uses an iterator to call the first version of erase,
note - cannot simply use itr++ in 'for' loop and ignore return iterator from erase,
because the value of itr is stale immediately after the call to erase, which is why
erase returns an iterator.
*/
iterator erase(iterator itr)
{
Node* p = itr.current;
iterator retVal{ p->next };
p->prev->next = p->next;
p->next->prev = p->prev;
delete p;
theSize--;
return retVal;
}
iterator erase(iterator from, iterator to)
{
for (iterator itr = from; itr != to;)
{
itr = erase(itr);
}
return to;
}
/*
data members for list - pointers to header and tail nodes. also keeps track of
size in a data member so that the size method can be implemented in constant time
*/
private:
int theSize;
Node* head;
Node* tail;
};
int main()
{
List<int> theList{};
for (int i = 0; i < 10; i++)
theList.push_back(i);
List<int>::iterator iter = theList.begin();
for(int i=0;i<5;i++)
std::cout << *(iter++) << std::endl;
}
I'm currently implementing a forward list (Singly linked list).
I noticed that std::forward_list::emplace_after is O(1), so the complexity is constant: How is this possible?
I'm asking because:
You can have the position of the new element to be in the middle,
As far as I know, the only way to find the position where a new element has to be added is to traverse the list with a loop.
Am I missing something?
This is how I currently implement the function.
constexpr void emplace_after(iterator position, Args...args) { // Must be O(1)
size_type index_position = std::distance(begin(), position);
Node* temp = m_head;
for (size_type index{ 0 }; index < index_position; ++index) {
temp = temp->next;
}
Node* next_temp = temp->next;
Node* current_node = new Node(std::forward<Args>(args)...);
temp->next = current_node;
current_node->next = next_temp;
temp = nullptr;
next_temp = nullptr;
m_size += 1;
}
And this is my current forward iterator implementation:
template<typename T>
struct forward_iterator {
Node* m_iterator;
using value_type = T;
using reference = value_type&;
using pointer = value_type*;
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
constexpr forward_iterator(Node* forw_iter = nullptr) : m_iterator{ forw_iter } {}
constexpr Node* getNodeAddress() const noexcept {
return m_iterator;
}
constexpr Node* getNodeNextAddress() const noexcept {
return m_iterator->next;
}
constexpr reference operator*() const noexcept {
return m_iterator->data;
}
constexpr pointer operator->() const noexcept {
return m_iterator;
}
constexpr forward_iterator& operator++() noexcept {
m_iterator = m_iterator->next;
return *this;
}
constexpr forward_iterator operator++(int) noexcept {
forward_iterator tmp(*this);
this = (this)->next;
return tmp;
}
constexpr friend bool operator== (const forward_iterator& first, const forward_iterator& second) noexcept {
return (first.m_iterator == second.m_iterator);
}
constexpr friend bool operator!=(const forward_iterator& first, const forward_iterator& second) noexcept {
return !(first.m_iterator == second.m_iterator);
}
};
You seem to have a misunderstanding about how iterators are supposed to work in singly linked lists. They are not just an identifier you can (and have to) search for, they should point to an actual node in one way or another.
Illustration:
Iterator ------------------+
|
V
[Head] -> Node -> Node -> Node -> Node -> Node -> nullptr
This means your class iterator should contain a Node * (Edit: As it already does)
Then your emplace_back can look like this:
constexpr void emplace_after(iterator position, Args... args)
{
Node* temp = position.getNodeAddress(); // retrieve the node position is referring
Node* next_temp = temp->next;
Node* current_node = new Node(std::forward<Args>(args)...);
temp->next = current_node;
current_node->next = next_temp;
temp = nullptr;
next_temp = nullptr;
m_size += 1;
}
The argument of std::forward_list<T,Allocator>::emplace_after is an iterator, which means you need to find the position before emplacing. Therefore, it just need constant time.
Iterators let you go to the node they refer to in O(1) time.
If your iterator does not, fix it.
I can't understand why I'm getting a conversion error on my Node ctor, the node constructor accepts 'const T&' as the first parameter and thats what my insert method is passing into the constructor but for some reason its still throwing me that error.
error C2440: 'initializing' : cannot convert from 'const Record' to 'SortedList<>::Node *
SortedList is of type 'Record' but if I type anything inbetween <> everything in between the brackets will disappear.
List class:
class SortedList{
struct Node{
T data_;
Node* next_;
Node* prev_;
Node(const T& data=T{},Node* nx=nullptr,Node* pr=nullptr){
data_ = data;
next_ = nx;
prev_ = pr;
}
};
Node* front_;
Node* back_;
public:
class const_iterator{
friend class SortedList;
protected:
Node* curr_;
const_iterator(Node* n){
curr_ = n;
}
public:
const_iterator(){
curr_ = nullptr;
}
const_iterator operator++(){
curr_ = curr_->next_;
return *this;
}
const_iterator operator++(int){ //advances curr returns old
const_iterator old = *this;
curr_ = curr_->next_;
return old;
}
const_iterator operator--(){
curr_ = curr_->prev_;
return *this;
}
const_iterator operator--(int){
const_iterator temp = *this;
curr_ = curr_->prev_;
return temp;
}
bool operator==(const_iterator rhs){
return curr_->data_ == rhs.curr_->data_;
}
bool operator!=(const_iterator rhs){
return curr_->data_ != rhs.curr_->data_;
}
bool operator<(const_iterator rhs){
return curr_->data_ < rhs.curr_->data_;
}
const T& operator*()const{
return curr_->data_;
}
};
Where the conversion error is being throw:
template <typename T>
typename SortedList<T>::iterator SortedList<T>::insert(const T& data){
Node* n(data);
iterator it_temp(n);
iterator sort(front_);
while (sort < it_temp){
++sort;
}
n.next_ = sort.curr_;
n.prev_ = sort.curr_->prev_;
sort.curr_->prev_->next_ = n;
sort.curr_->prev_ = n;
}
The error is specifically being throw where Node* n is being constructed in the insert function.
You cannot construct a pointer like this
Node* n(data);
You'd have to, for example, use new
Node* n = new Node(data);
I am trying to implement Linked List with custom iterator.
I am getting a bunch of errors trying to implement copy constructor:
'LinkedListIterator>' : no appropriate default constructor available
'LinkedListIterator> LinkedList::begin(void)' : cannot convert 'this' pointer rom 'const LinkedList' to 'LinkedList &'
'LinkedListIterator> LinkedList::end(void)' : cannot convert 'this' pointer from 'const LinkedList' to 'LinkedList &'
LinkedList
class LinkedList
{
std::unique_ptr<node> head;
std::unique_ptr<node> tail;
LinkedList(const LinkedList& other)
{
init();
iterator i = other.begin();
while (i != other.end())
add(*i++);
head = other.head;
tail = other.tail;
}
iterator begin()
{
return iterator(head->next);
}
iterator end()
{
return iterator(tail);
}
Iterator
template <typename TNode>
class LinkedListIterator
{
friend class LinkedList<typename TNode::value_type>;
TNode* p;
public:
LinkedListIterator(TNode* p) : p(p) {}
LinkedListIterator(const LinkedListIterator& other) : p(other.p) {}
LinkedListIterator& operator=(LinkedListIterator other) { std::swap(p, other.p); return *this; }
void operator++() { p = p->next; }
void operator++(int) { p = p->next; }
bool operator==(const LinkedListIterator& other) { return p == other.p; }
bool operator!=(const LinkedListIterator& other) { return p != other.p; }
int& operator*() { return p->data; }
LinkedListIterator<TNode> operator+(int i)
{
LinkedListIterator<TNode> iter = *this;
while (i-- > 0 && iter.p)
{
++iter;
}
return iter;
}
};
}
Let me know if you need me to post more code. Thanks.
Member functions begin() and end()are defined as non-constant member functions
iterator begin()
{
return iterator(head->next);
}
iterator end()
{
return iterator(tail);
}
However you call them for const object other
LinkedList(const LinkedList& other)
{
init();
iterator i = other.begin(); // <== here
while (i != other.end()) // <== here
add(*i++);
head = other.head;
tail = other.tail;
}
As for error mesdsage
no appropriate default constructor available
then I do not see where the default constructor is used. Nevertheless the error message is clear enough: class LinkedListIterator<Node<T>> has no the default constructor but some where in the code you create an object of this type using the default constructor.
Assuming that 'iterator' is defined as 'LinkedListIterator' it appears that you are attempting to pass the 'head' and 'tail' variables (which appear to be global?) to constructors that do not exist and the compiler is making a last ditched effort to match against a copy constructor (for which there is no match).
I would assume that code should be as follows:
iterator begin()
{
return iterator(head->next.get());
}
iterator end()
{
return iterator(tail.get());
}
I work on own iterator, that should iterate double linked list. If I'm iterating, for cycle is immediately skipped. I haven't errors, but from debugging I don't read much.
for cycle:
for(SemestralWork::DoubleList<Student>::iterator it = link->begin(); it != link->end(); ++it){
//something...
}
iterator + begin() + end():
class iterator {
private:
Node<T> * node;
public:
iterator(){}
iterator(const iterator& cit){}
iterator(T t) {
}
~iterator(){}
iterator& operator=(const iterator& second){
node = second.node;
return(*this);
}
iterator& operator++(){
if (node != NULL){
node = node->GetNext();
}
return(*this);
}
iterator operator++(int){
iterator tmp = *this; //iterator tmp(*this)
operator++();
return tmp;
}
bool operator==(const iterator& second) {
return node == second.node;
}
bool operator!=(const iterator& second) {
return node != second.node;
}
T& operator*() {return node->GetData();}
T* operator->(){return((DoubleList<T>::iterator)*this);}
};
iterator begin(){
return iterator(first->GetData());
}
iterator end(){
return iterator(last->GetData());
}
Node:
template <class U>
class Node{
Node<U> * next;
Node<U> * previous;
U data;
public:
Node(const U &data){
next = NULL;
previous = NULL;
this->data = data;
}
void SetNext(Node<U> *next) { this->next = next; }
Node<U> *GetNext(){ return next; }
void SetPrevious(Node<U> *previous) { this->previous = previous; }
Node<U> *GetPrevious(){ return previous; }
U &GetData() { return data; }
};
Here are a few things I noticed. Whether any of these will actually resolve your problem I don't know because the posted code is incomplete:
Your iterator should not be constructed from a T object and the implementation of the constructor should actually do something (I would guesss the fact that iterator::iterator(T) doesn't do anything at all is your actual problem). Instead, the iterator should be constructed from an Node<T>*.
The preincrement operator should not check if there is actually a next element! It is a precondition for the operator++() that the iterator can be incremented. If anything, the operator should report a misuse with debug settings enabled.
I'm suspicious of your use of last: note that the end iterator is a position one past the last element.
Your comparision oerators should be const members and typically operator!=() just delegates to operator==(), i.e.:
bool iterator::operator!=(iterator const& other) const {
return !(*this == other);
}
The advantage of this implementation is that the operator!=() is consistent with the operator==() even if the implementation of operator==() is changed.
You didn't post the list implementation showing first and last, but I'm assuming last points at the last element. With iterators, end() should point beyond the last element, not to the last element. For example, if the list contains exactly 1 element, your for-loop won't run at all since first == last and therefore begin() == end().