Working on a tree class with an STL-like interface, I encountered a problem using non default-constructible elements:
As I'm implementing iterators, I need a past-the-last element at any point in time. My approach is to create one in the constructor, which asserts the value type to be default constructible.
Is there an approach to get rid of this constraint?
Pointing past the end can't possibly be nullptr if your iterator is bidirectional, end()-- needs to be legal.
It can be instead implemented by a sentinel, and in this case, the sentinel shouldn't even contain a default constructed element.
This can be done as such
struct link
{
link *parent, *left, *right;
};
template<typename T>
struct node : link
{
T data;
};
template<typename T>
struct tree : link
{
// tree itself serves as the sentinel
// At initialization parent and childs should all point to the sentinel
tree() : parent(this), left(this), right(this) {}
// ...
};
And iterators need no special handling for one past the end case.
// nested within tree
struct iterator
{
explicit iterator(link* l) : n(l) {}
iterator& operator--() { n = n->parent; return *this; } // or something else
auto& operator*() { return reinterpret_cast<node<T>*>(n)->data; }
// ...
link* n;
};
iterator begin() { return {left}; } // or something else
iterator end() { return {this}; }
I solved the problem by storing another pointer to the root node, from which I can recreate the last element in constant time.
Related
I have an iterator class nested in a LinkedList class. My question is how do I make the insert_after function using iterators. The rest of the code is given for information purposes, but the function I'm trying to get working is at the end.
Insert_After takes a position and inserts a value after it.
template <typename T>
class LinkedList : public LinkedListInterface<T> {
private:
struct Node {
T data; // data can be any type
Node* next; // points to the next Node in the list
Node(const T& d, Node* n) : data(d), next(n) {}
};
Node* head; // Is a pointer
class Iterator
{
private:
Node* iNode;
public:
Iterator(Node* head) : iNode(head){ }
~Iterator() {}
bool operator!=(const Iterator& rhs) const { return iNode != rhs.iNode; }
Iterator& operator++() { iNode = iNode->next; return *this; }
T& operator*() const { return iNode->data; }
};
/** Return iterator pointing to the first value in linked list */
Iterator begin(void) {
return LinkedList<T>::Iterator(head);
}
/** Return iterator pointing to something not in linked list */
Iterator end(void) {
return LinkedList<T>::Iterator(NULL);
}
/** Return iterator pointing found value in linked list */
Iterator find(Iterator first, Iterator last, const T& value) {
Iterator current = first;
bool found = false;
while (current != last) {
if (*current == value) {
return current;
}
++current;
}
return last;
}
Iterator insert_after(Iterator position, const T& value)
{
// Need help here
}
What I've tried so far resulted in a few errors.
Iterator insert_after(Iterator position, const T& value)
{
// Need to insert after position
Iterator previous = position;
++position;
Node* newNode = new Node(value, position);
previous->next = newNode;
}
The error I got was Error C2664 'function' : cannot convert argument n from 'type1' to 'type2' for the line
Node* newNode = new Node(value, position);
Compiler Error C2819 type 'type' does not have an overloaded member 'operator ->' for line
previous->next = newNode;
I understand the errors but I'm not sure how to work around them.
I think the short answer to your compiler errors is that you are likely supposed to pass a Node or Node* as your second argument and not an iterator. previous is also an iterator, and therefore does not have a next call.
Long answer below about generally fixing the function in question:
There's a lot [not] going on in that function, which gets me wondering about the rest of the linked list class as well. I've only looked at this function, as it's the one you claim is causing you trouble.
SUBJECTIVE THOUGHT I generally hate working with iterators in my class functions. Deal with the Nodes directly as much as possible. The iterator pattern exists for universal traversal of containers no matter how they're laid out, and that abstraction makes it a pain to deal with inside your class.
Iterator insert_after(Iterator position, const T& value)
{
// Need to insert after position
Iterator previous = position;
++position;
Node* newNode = new Node(value, position);
previous->next = newNode;
}
As it currently stands, if position is anywhere but the last element, you will break your list and leak memory. This is because you never check what is after position. That first mistake leads into the second. newNode->next never gets set. Maybe it's default constructed to nullptr, that's fine. But if I'm inserting into the middle of my list, I need to connect newNode to whatever came after position originally.
Another question that you need to consider is "what if this is called on an empty list?" Does your begin() function handle that? Is it supposed to throw?
Iterator insert_after(Iterator position, const T& value)
{
Node* pos = position.iNode;
if (!pos) { // assumes an empty list if this is true
// Correctly build first Node of your list and get everything assigned
// that you can
// return the new iterator;
// or just throw
}
if (!pos->next) { // position at end of list if true
pos->next = new Node(value, position); // Is that second argument
// supposed to be an iterator?
return Iterator(pos->next);
} else {
// Some of this is probably redundant depending on how you are actually
// building Nodes, but the gist is it's important to ensure the list is
// not broken; connecting tmp before changing existing nodes helps the
// list stay intact for as long as possible
Node* tmp = new Node(value, position);
tmp->next = pos->next;
pos->next = tmp;
return Iterator(tmp);
}
A doubly linked list never seems attractive at first blush to a student, but it makes certain operations like erasing from the middle of the list so much easier. Yes, you have one extra pointer to deal with, but it makes it harder to lose Nodes as well. Along with bi-directional iteration.
I am trying to implement set as dynamic vector, but I don't know how to increment my iterator.
My code:
#pragma once
class node
{
public:
elem e;
node(){}
node(elem e):e(e){};
node(const node<elem>& n)
{
this->e=n->e;
}
};
class iteratorr
{
public:
node<elem>* current;
iteratorr():current(NULL){};
iteratorr(Node<elem>* c):current(c){};
iteratorr(const Iterator<elem>& it)
{
this->current=it.current;
}
iteratorr<elem>& operator++()
{
}
elem elem(){return current->e;}
};
Both classes are template typename elem. For some reason I can't write it in the code.
If I would add a pointer to a next node in class node, that would become a linked list I guess. How could I increment my iterator?
I want to make an iterator that can handle my selfmade structs:
struct node {
int n; //data element
node * parent;
node * left;
node * right;
node (int elem): n(elem){ //create root
parent = NULL; left = NULL; right = NULL;
}
node (int elem, node* p): n(elem), parent(p) { //addchild
left = NULL;
right = NULL;
}
node(int elem, node * p, node * l, node * r): n(elem), parent(p), left(l), right(r){ //insert element
}
};
First of all, is this possible? If yes: how do I start to make the iterator that traverses the tree. If not: what do you suggest for me to do if I want to access data elements in the list.
Yes.
Hint: Your iterator, when it advances, should go to the parent and figure out whether it's a left child or right child of that parent. That will tell you where to go next.
Yes, given the node structure you've outlined with a right pointer (which presumably points to the next node in traversal order), implementing a forward iterator should be quie easy.
The basic idea is something like this:
template <class Node>
class iterator {
Node *pos;
public:
iterator(Node *tree = nullptr) : pos(tree) {}
iterator operator++() { pos = pos->right; return *this; }
Node &operator*() { return *pos; }
bool operator!=(iterator const &other) const { return pos != other.pos; }
};
There is a little more than that involved in a real iterator though. In particular, you normally (always?) want to specify things like the value_type, reference_type and category of the iterator. Most of these are pretty close to pure boilerplate though, and you can handle most of them by deriving from std::iterator.
You also (typically) want to support a few more operations than I've shown here, such as post-fix increment, operator==, etc. Given this as a "framework", I think filling in those blanks should be fairly straightforward though.
I should probably also point out the existence of the Boost iterators library. This simplifies the task of implementing an iterator (somewhat) and cuts down on the boilerplate you have to write (quite a bit).
I've got a linked list where I save data, and a pointer to next node, Node<T>* next, like this:
template <class T>
struct Node
{
T data;
Node<T>* next;
};
The thing is I want to put in this a post-increment operator, so it returns the previous value of my node, but increment the reference. So if I do this
Node<int>* someNode = someList.SomeNode();
Node<int>* tmp = someNode++;
tmp would be the original someNode value, but someNode would be someNode->next.
is it possible to put an operator in the struct? I've tried to, and searched how to do it, but as I don't deal with operators I don't know how to do.
You cannot add member function to basic type like pointer.
What are you trying to define is an iterator. Use wrapper class over your node pointer to succeed:
template <class T>
struct NodeIterator
{
NodeIterator(Node<T>* current) : current(current) {}
NodeIterator& operator ++() { current = current->next; return *this; }
NodeIterator operator ++(int) {
NodeIterator retVal = *this;
++(*this);
return retVal;
}
T* operator-> () const { return ¤t->data; }
T& operator * () const { return current->data; }
Node<T>* current;
};
See std::slist<> implementation for references. Look at template<typename _Tp> struct _List_iterator. Reading STL implementation is better than many books.
Usage:
NodeIterator<T> it = &node;
++it;
T& t = *it;
Node<T>& operator++(int) {…}
is the member you want to implement.
For your code to work, you'd need to be able to define operator++ for your pointer class. That's not allowed, though. You're welcome to define some other named function, though. For example:
template <typename Node>
Node goto_next(Node& node) {
Node result = node;
node = node->next;
return result;
}
Then you can use it like this:
Node<int>* tmp = goto_next(someNode);
Another option is to provide a real iterator class instead of just using a pointer:
Node<int>::iterator someNode = someList.begin();
Node<int>::iterator tmp = someNode++;
Make your iterator keep a Node<T>* member, and make the ++ operator update that internal pointer before it returns a copy of the iterator object.
You really don't want to do that. The idea of using ++ on a pointer is dangerously close to the common iterator pattern. You should just go the full distance and make a real iterator class. Think of std::list<T>::iterator.
Iterators are very lightweight wrappers to give a sensible interface to a node pointer, which provides things like operator ++ to move to the next node, and overloads operator -> to provide simple access to the node data. Converting client code from using a pointer to using an iterator is very straight-forward because the syntax is almost identical.
I don't get what Bjarne has on mind stating:
A list iterator must be something more complicated than a simple
pointer to an element because an element of a list in general does not
know where the next element of that list is. Thus, a list iterator
might be a pointer to a link
I know that list's node has pointers to the next and previous nodes. So how it "in general does not know"?
For contrast, let's consider an iterator for std::vector. Though you probably don't want to, you could basically implement it as a simple pointer:
template <class T>
class vector {
T *data;
size_t current_size;
size_t alloc_size;
public:
typedef T *iterator;
iterator begin() { return data; }
iterator end() { return data+current_size; }
// ...
};
and since the data in the vector is contiguous, when you do (for example) ++ on the iterator, it would do the right thing (get you to the next item in the vector).
With a linked list, it can't be that simple though -- if you used typedef to make iterator an alias for T *, it wouldn't work right. When you tried to do ++ on the iterator, it would just increment the pointer, instead of taking you to the next element in the linked list like it needs to.
You need to build some intelligence into the iterator class so operator++ (for one example) knows to "chase" the pointer instead of just incrementing.
template <class T>
class list {
class node {
T data;
node *next;
node *prev;
};
public:
class iterator {
node *pos;
public:
iterator operator++() {
return pos = pos->next;
}
iterator operator--() {
return pos = pos->prev;
}
};
};
Don't confuse list node with a list element. It might be a pointer, but to a node, which contains element and next/prev links. If it were just a pointer to the element, you wouldn't be able to move around with it. (Though it's unlikely to be just pointer, since it typically has to overload operator++ to fetch next pointer instead of incrementing itself).
This isn't true for intrusive lists, where node == element, but that's not std::list.
// illustrative
template <typename ElementType>
struct list_node {
ElementType element;
list_node *next, *prev;
};