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
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
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;
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).
I wrote the following code when trying to make a doubly-linked list with an internal STL-like iterator. I'll just provide the header file with the non-relevant parts trimmed out for now.
My questions are...
The STL uses iterators in a certain way - specifically, you navigate over an STL container from the .begin() iterator up to but not including the .end() iterator. To do this the .end() iterator has to be one-past the end of the container. How would I implement this kind of semantic given what I've started with (this is the main question)?
Is there anything missing in the interface as it stands (with regard to the iterator class and things that should be present in it)?
Here's the code:
template <typename T>
class Node
{
T data;
Node<T>* next;
Node<T>* prev;
};
template <typename T>
class LinkedList
{
public:
class Iterator
{
public:
Iterator() {}
explicit Iterator(const Node<T>& init) { current = init; }
//Dereference operator - return the current node's data.
inline T& operator*() { return current->data; }
//Prefix returns by reference.
inline Iterator& operator++() { current = current->next; return *this; }
inline Iterator& operator--() { current = current->prev; return *this; }
//Postfix returns non-reference and has int parameter to differentiate function signature.
inline Iterator operator++(int) { Iterator res = *this; current = current->next; return res; }
inline Iterator operator--(int) { Iterator res = *this; current = current->prev; return res; }
private:
Node<T>* current;
};
Iterator begin() { return Iterator(m_start); }
Iterator end() { return Iterator(m_end); }
private:
Node<T>* m_start;
Node<T>* m_end;
};
I'm aware that I may or may not have problems with the ++/-- operators, but that doesn't particularly bother me as I'll work those out when I have enough code to do some testing on this. Feel free to drop hints though if you're inclined :)
The iterator returned by end() must be decrementable, otherwise you cannot iterate over the list in reverse.
You could do this, by having Iterator store two pointers: to current node (which would be NULL for end) and to previous node (which allows you to find the last node with data in it, even if current == NULL.
class Iterator
{
public:
Iterator() {}
explicit Iterator(Node<T>* curr, Node<T>* prev):
current(curr), previous(prev) {}
//Dereference operator - return the current node's data.
inline T& operator*() { return current->data; }
//Prefix returns by reference.
inline Iterator& operator++()
{
previous = current;
current = current->next;
return *this;
}
inline Iterator& operator--()
{
current = previous;
previous = current->previous;
return *this;
}
//Postfix should be implemented in terms of prefix operators
inline Iterator operator++(int) { Iterator res = *this; ++*this; return res; }
inline Iterator operator--(int) { Iterator res = *this; --*this; return res; }
private:
Node<T>* current;
Node<T>* previous;
};
Iterator begin() { return Iterator(m_start, 0); }
Iterator end() { return Iterator(0, m_end); }
Alternatively you can have your list contain a sentinel node that designates the "one-past-end" of the list. This node should not have the data member though. This can be achieved by splitting the Node class into a non-template base with only pointers to next and previous node.
For example, it appears that GCC's list implementation stores a pointer to the sentinel, so that its next points to the first item in the list and its prev points to the last item in the list (or both point to itself, if the list is empty).
You are missing operator->, operator== and operator!=, the classification typedefs which can be inherited from std::iterator, a const_iterator implementation (iterator should be implicitly convertible to const_iterator).
First node's prev pointer is NULL, so is last node's next pointer. One-past-the-end's current pointer would be NULL.
operator->
I think NULL will fit here just great.
You may want to write something like for (iterator it = list.begin(); it != list.end(); it++), so you need comparison operators to be defined too.