Here is the declaration:
Iterator operator++();//pre-increment
Here is the definition:
LinkedList::Iterator& LinkedList::Iterator::operator++(){
return Iterator(current->next);//this is giving me an error
}
Here is how the class looks
class LinkedList{
public:
Node struct{
/*contains pointer to next node and an integer value*/
int val;
Node* next;
};
class Iterator{
public:
Iterator& operator++();//pre-increment
protected:
Node* current;//points to current node
}
}
You create a new iterator object, and (attempt to) return a reference to that.
The prefix-increment operator modifies this object, and should return a reference to itself:
current = current->next; // TODO: Add checking for not going out of bounds or dereferencing a null pointer
return *this;
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 have a class containing such a structure:
class Store {
private:
struct pointNode {
T data;
std::unique_ptr<pointNode> next;
pointNode():data(std::move(T{})),next(std::move(std::unique_ptr<pointNode>{})){}
pointNode(T n_data, std::unique_ptr<pointNode> ptr) : data(std::move(n_data)), next(std::move(ptr)) {}
};
std::unique_ptr<pointNode> head;
public:
decltype(auto) Begin() ;
};
I need to create a Begin function that will get a pointer to head.
I wrote something like this:
template<typename T>
decltype(auto) Stack<T>::Begin()
{
std::shared_ptr<pointNode> sh_ptr=std::make_shared<pointNode>(head);
return std::move(sh_ptr);
}
That is, create a shared_ptr and return it from the function, then go through the list.
But:
std::shared_ptr<pointNode> sh_ptr=std::make_shared<pointNode>(head);
returns error C2664
Error C2664 "Stack<int>::pointNode::pointNode(Stack<int>::pointNode &&)":
argument 1 cannot be converted from "std::unique_ptr<Stack<int>::pointNode,std::default_delete<Stack<int>::pointNode>>"
in "const Stack<int>::pointNode &"
I tried to remove decltype (auto), but everything is the same .Maybe someone understands what I'm doing wrong .I would be happy to help, thank you
unique_ptr cannot share the data with shared_ptr. That's the point of a unique pointer. But if you want to transfer the ownership, you can do it like so:
#include <string>
#include <memory>
int main()
{
std::unique_ptr<std::string> up( new std::string("Hello, World!\n") );
// up owns the object
// Transfer ownership
std::shared_ptr<std::string> sp( up.release() );
// sp owns the object, up is empty.
}
Having said that, returning a shared_ptr from the begin() function is likely not what you want. I would guess that you want to return a reference or an iterator from that function. Here's how you can return a reference:
template<typename T>
pointNode& Stack<T>::begin()
{
return *head;
}
Note that, for this to work like STL iterators, you need to create a new iterator type. begin returning a refence to a private node class is not a great interface and leaks implementation details.
unique_ptrs claim unique ownership. The clean way to transfer ownership to a shared_ptr would be to move it. So if you have
std::unique_ptr<pointNode> head;
then
std::shared_ptr<pointNode> sh_ptr( std::move(head) );
This is however a design flaw.
You do not want to transfer ownership of the container's elements. Here you actually want to share the raw pointer with the iterator. Any iterator that you have should be able to use that raw pointer - but not destroy the element it points at.
The first draft of a simple forward list type of container would be something like the below - and nowhere should a unique_ptr or shared_ptr be used:
template<class T>
class Store {
private:
struct Node {
T data;
Node* next;
};
Node* head = nullptr;
public:
struct iterator {
iterator(Node* x = nullptr) : current(x) {}
T& operator*() { return current->data; }
T* operator->() { return ¤t->data; }
iterator& operator++() { current = current->next; return *this; }
bool operator!=(const iterator& rhs) const {
return current != rhs.current;
}
private:
Node* current;
};
~Store() {
Node* next;
while(head) {
next = head->next;
delete head;
head = next;
}
}
iterator begin() { return head; }
iterator end() { return nullptr; }
};
Demo
I'm trying to implement overloading of the subscript operator in my doubly linked list class, but I am facing problems I've been unable to overcome by myself. I am also pretty new to C++.
This is what I have now.
Outtake from DList class:
T &operator[](int index) {
lookAt = root;
for (size_t i = 0; i < index; i++) {
lookAt = lookAt->getNext();
}
return lookAt->getItem();
}
Node<T>* root;
Node<T>* lookAt;
Node<T>* temp;
Node class:
template <class T>
class Node {
public:
Node() {
this->setNext(nullptr);
this->setPrev(nullptr);
}
Node *getNext() const {
return next;
}
void setNext(Node *next) {
Node::next = next;
}
Node *getPrev() const {
return prev;
}
void setPrev(Node *prev) {
Node::prev = prev;
}
T getItem() const {
return item;
}
void setItem(T item) {
Node::item = item;
}
private:
Node* next;
Node* prev;
T item;
};
The error I keep getting is this:
invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int' return lookAt[index].getItem();
Which leads me to believe there's some kind of problem with the way my item variable is referenced, or/and the return part of the overloading function.
Would appreciate any help/guidance with this.
Cheers
Your getItem() returns a T-type variable, which is a copy of the real data, item. (rvalue)
Your [] operator tries to return an T&, which is an lvalue (reference). However, we can't transform rvalue into lvalue.
One possible solution is to return T& in getItem(), like this:
T& getItem() {
return item; // Now returns a reference (lvalue).
}
Hope it helps.
I have a class like so:
class Node {
// data
Node* next;
void operator++(int);
};
And when I define the post-increment operator like so:
void Node::operator++(int) {
this = this->next;
}
I get the error Expression is not assignable on this = this->next;.
What's up with this? How do I get this to point to next?
Don't even try. It won't work, and if you could make it work, it would be a bad thing anyway.
From the looks of things, your node is in something like a linked list. If that's the case, then what you normally want is something like:
template <class T>
class linked_list {
class node {
node *next;
T data;
};
node *head;
public:
class iterator {
node *n;
public:
iterator(node *n=NULL) : n(n) {}
iterator &operator++() {
n=n->next;
return *this;
}
bool operator!=(iterator const &other) const {
return n != other.n;
}
// ...
};
iterator begin() { return iterator(n); }
iterator end() { return iterator(); }
};
This can not be changed. it is constant.
According to the C++ standard the this pointer is a prvalue expression (see [class.this]), however the assignment operator requires an lvalue as its left operand (see [expr.ass]).
You cannot do that. Even if by some dirty tricks you change this, the change won't be propagated to the caller. this is a hidden formal argument of methods.
You just discovered why the STL has iterators. An iterator is holding a pointer to a node as a member of itself or in some cases it is a pointer. It could also modify it's member variable. Maybe try something like:
class Iterator {
Node* node;
public:
Iterator(Node* n)
: node( n )
{}
Iterator& operator++()
{
node = node->next;
return *this;
}
Iterator operator++(int)
{
Node* current = node;
node = node->next;
return Iterator(current);
}
Node& operator*()
{
return *node;
}
const Node& operator*() const; // etc...
};
You seem very confused in terms of what you are trying to do here. You have a node class which represents a node in the list. Typically one would also have an iterator class which represents the position in the list that you are currently at. It seems to me that you are trying to use ++ to iterate to the next item in a list, but to do that you would need the iterator as a class of it's own.
In general it makes no sense to assign to the this pointer - this refers to the current object context, you can't just change that context by setting the this pointer.
I hope this will help you somewhat.
The hidden parameter 'this' is passed with the 'const' qualifier. Hence it cannot be changed.
Hidden this
I have the following class:
class list {
private:
struct node {
node() { data=T(); prev=next=this; }
˜node() {}
T data;
node *prev;
node *next;
};
public:
class iterator {
public:
iterator() : p(NULL) {}
T & operator*() { return p->data; }
iterator & operator++()
{ p = p->next; return *this; }
iterator & operator++(int)
{ iterator tmp = *this; ++*this; return *tmp; }
bool operator==(const iterator & rhs) const
{ return p == rhs.p; }
bool operator!=(const iterator & rhs) const
{ return p != rhs.p; }
private:
friend class list<T>;
iterator(node *p) : p(p) {}
node *p;
};
iterator begin() { return iterator(head->next); }
iterator end() { return iterator(head); }
list();
˜list();
etc ...
private:
int N;
node *head;
node *findnode(int);
};
I see that the begin() function returns a constructor for the iterator class. Does this mean that a new iterator is created when it is called? If so, will the memory be recycled after the variable that this iterator is assigned to goes out of scope? I'm slightly confused, for the iterator constructor has no return type. If someone could shed some light on my issues, I would be most appreciative.
Yes a new iterator is created when begin is called. At a high level, yes the memory the iterator occupies will be recycled after it goes out of scope.
Constructors do not have return types as they are called in place on the memory they are to initialize.
At a somewhat lower level, variables that are declared on the stack have their destructor called when they go out of scope, which is one aspect of "recycling". The memory they occupy is on the stack rather than in the heap (as it would be if new were called), so it doesn't get freed in the sense of doing a delete or garbage collection. Rather the memory the variable had occupied may sit idle for a while or be immediately overwritten depending on if stack frames are being removed or added immediately after it goes out of scope (i.e the functions are returning up the stack or new calls are being made).