class Iterator;
class SortedList {
friend class Iterator;
private:
Node* root;
Node* minim(Node* node) const {
while (node->getLeft() != nullptr)
node = node->getLeft();
return node;
};
public:
SortedList() {
root = nullptr;
};
Iterator iterator() {
Node* min = minim(root);
return Iterator(*this, min); // Here says that Iterator has no constructors
};
};
class Iterator {
private:
const SortedList& list;
Node* current;
public:
Iterator(const SortedList& list, Node* current) : list{ list }, current{ current } {};
};
It says int the iterator method of SortedList that the Iterator class doesn't have constructors also doesn't say about matching or not and if i modify some parameters to be incorrect it does specify that there aren't any with those parameters.
Another thing, if i comment the iterator method out and instantiate an iterator in main by writing Iterator it { my parameters} it works just fine.
Iterator is an incomplete type when you use:
Iterator iterator() {
Node* min = minim(root);
return Iterator(*this, min); // Here says that Iterator has no constructors
};
Move the definition of the function after definition of Iterator.
class Iterator;
class SortedList {
friend class Iterator;
private:
Node* root;
Node* minim(Node* node) const {
while (node->getLeft() != nullptr)
node = node->getLeft();
return node;
};
public:
SortedList() {
root = nullptr;
};
// Just the declaration
Iterator iterator();
};
class Iterator {
private:
const SortedList& list;
Node* current;
public:
Iterator(const SortedList& list, Node* current) : list{ list }, current{ current } {};
};
// Now the definition
Iterator SortedList::iterator() {
Node* min = minim(root);
return Iterator(*this, min);
};
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 days ago.
Improve this question
I have made this doubly linked list class in C++ , it utilises sentinel nodes as well in it , I was making an insert(iterator it , T& value) function in the list , which inserts a new node before the node that is specified by the iterator it . I was testing the code , it wasn't working at the end cases i.e when the iterator had its value as the starting node and the ending node
Doubly Linked List class
template <typename T>
class DoubleList
{
struct Node
{
Node *prev;
Node *next;
T data;
Node(const T &data = T{}, Node *next = nullptr, Node *prev = nullptr)
{
this->data = data;
this->next = next;
this->prev = prev;
}
};
Node *front;
Node *back;
int size;
public:
class const_iterator
{
public:
const_iterator() {}
const_iterator &operator++() {}
const_iterator operator++(int) {}
const_iterator &operator--() {}
const_iterator operator--(int) {}
bool operator==(const_iterator rhs) {}
bool operator!=(const_iterator rhs) {}
const T &operator*() const {}
};
class iterator : public const_iterator
{
public:
iterator() {}
iterator &operator++() {}
iterator operator++(int) {}
iterator &operator--() {}
iterator operator--(int) {}
T &operator*() {}
const T &operator*() const {}
};
iterator begin()
{
return iterator(this->front->next, this);
}
iterator end()
{
return iterator(this->back, this);
}
const_iterator cbegin() const
{
return const_iterator(this->front->next, this);
}
const_iterator cend() const
{
return const_iterator(this->back, this);
}
Constructor for the linked list
template <typename T>
DoubleList<T>::DoubleList()
{
front = new Node();
back = new Node();
front->next = back;
back->prev = front;
}
template <typename T>
DoubleList<T>::~DoubleList()
{
Node *curr = this->front;
while (this->curr != back)
{
Node *next = curr->next;
delete curr;
curr = next;
}
delete this->front;
delete this->back;
this->size = 0;
}
iterator function
class iterator : public const_iterator
{
friend class DoubleList;
iterator(Node *n, const DoubleList *linkedList)
{
const_iterator(n, linkedList);
}
public:
iterator()
{
const_iterator();
}
iterator &operator++()
{
if (this->curr)
{
this->curr = this->curr->next;
}
return *this;
}
iterator operator++(int)
{
iterator old = *this;
if (this->curr)
{
this->curr = this->curr->next;
}
return old;
}
iterator &operator--()
{
if (this->curr)
{
this->curr = this->curr->prev;
}
else
{
this->curr = this->list->back;
}
return *this;
}
iterator operator--(int)
{
iterator old = *this;
if (this->curr)
{
this->curr = this->curr->prev;
}
else
{
this->curr = this->list->back;
}
return old;
}
T &operator*()
{
return this->curr->data;
}
const T &operator*() const
{
return this->curr->data;
}
Insert() function
template <typename T>
typename DoubleList<T>::iterator DoubleList<T>::insert(iterator it, const T &data)
{
Node *nn;
if (front->next != back)
{
nn = new Node(data, it.curr, it.curr->prev);
it.curr->prev->next = nn;
it.curr->prev = nn;
}
else
{
nn = new Node(data, back, front);
front->next = nn;
back->prev = nn;
}
this->size_++;
return iterator(nn, this);
}
the error occurs when i create a new list and insert a node into it
when i check wether end() and begin() point to different nodes in the linkedList ,it points to the same node , meaning that the node was never added into the list , what can i correct here to make it work fine?
a minimal reproducible example can be done by the following code
int main(){
DoubleList<int> theList;
DoubleList<int>::iterator it1 = theList.insert(theList1.begin(), 1);
if (theList.begin() == theList.end())
{
std::cout<<"Error recieved"<<std::endl;
}
}
I've created a Linked List in C++ and want to implement an iterator for it so that I can do range loops: for (const int& i : list) where Linked_List<int> list;.
My idea is to create the Iterator as part of the Linked_List class like this:
This is what I got so far:
template <typename T>
class Linked_List
{
public:
struct Iterator;
struct Node;
public:
Linked_List();
~Linked_List() noexcept(false);
Linked_List(const Linked_List&) = delete;
Linked_List(Linked_List&&) = delete;
Linked_List& operator=(const Linked_List&) = delete;
Linked_List& operator=(Linked_List&&) = delete;
void push_back(T);
void push_front(T);
void pop_back();
void pop_front();
bool empty() const;
T back() const;
T front() const;
//void swap(T, T);
//void insert(Iterator, T);
//void erase(Iterator);
//Iterator begin() const;
//Iterator end() const;
private:
Node* head;
Node* tail;
};
template<typename T>
struct Linked_List<T>::Node
{
Node() : prev(nullptr), next(nullptr) {}
Node(T t) : value(t), prev(nullptr), next(nullptr) {}
Node* prev;
Node* next;
T value;
};
Is this a good approach?
Should I do error checking when incrementing the list to check if current->next == tail? If so, how do I do that? Because my Iterator doesn't have a list object with a tail.
Edit:
I'm not sure how to implement the struct Iterator;, I get stuck when figuring out how to connect it with the list so that I can check if the current node returned from the iterator equals the tail in the list, in the Linked_List Iterator end() const method.
Let's say I've implemented all the necessary operators for an iterator like this:
struct Iterator
{
T& operator*() const { return current->value; }
bool operator!=(const Iterator& rhs) { return (*_current != rhs._current); }
Iterator& operator++()
{
current = current->next;
return *this;
}
};
How would I go about implementing Iterator Linked_List<T>::begin() const; and end() now?
I imagine an imaginary user making an iterator object like this:
Linked_List<int>::Iterator it;
An idea is to have a public constructor with no parameters and a private constructor that takes a node as a parameter which _current will be set to, and have the Linked_List class as a friend.
A few notes.
There are two options where to declare Node and Iterator. Inside the list class as List<T>::Node or outside as Node<T>. It is, in part, a matter of taste. From engineering perspective though, the symbol names are longer for nested classes, so your debuginfo is bigger. Also, when nested classes are also templates it is harder to specialize them if/when necessary (because that requires fully specializing the enclosing template first), but this is not the case here.
It leads to more elegant code when one list node is used as list head and tail. Empty list is a node whose next and prev point to itself. push_front appends to list.next which points to the first node or itself. push_back appends a node to list.prev which points to the last node or itself. When inserting/removing nodes there is no need to have special handling of the first and last nodes. E.g. :
struct Node {
Node *next_, *prev_;
Node()
: next_(this), prev_(this)
{}
~Node() {
unlink();
}
void push_back(Node* n) {
n->next_ = this;
n->prev_ = prev_;
prev_->next_ = n;
prev_ = n;
}
void unlink() {
Node *next = next_, *prev = prev_;
next->prev_ = prev;
prev->next_ = next;
next_ = this;
prev_ = this;
}
};
In the above, Node only needs two operations to be able to maintain a list. More than that, Node is itself a minimalist list that can be used for intrusive lists (with auto-unlink in the destructor). Note how using this makes checks for nullptr unnecessary - Node is always a valid list.
Error checking should be in debug mode only (use assert, for example). Otherwise, those checks penalise correct applications with unnecessary run-time checks.
Here is a minimal working example based on the ideas for you:
template<class T>
class List;
class Iterator;
class Node {
friend class Iterator;
template<class T> friend class List;
protected:
Node *next_, *prev_;
void push_back(Node* n) {
n->next_ = this;
n->prev_ = prev_;
prev_->next_ = n;
prev_ = n;
}
void unlink() {
Node *next = next_, *prev = prev_;
next->prev_ = prev;
prev->next_ = next;
next_ = this;
prev_ = this;
}
public:
Node()
: next_(this), prev_(this)
{}
~Node() { unlink(); }
};
class Iterator {
protected:
Node* node_;
Iterator(Node* node)
: node_(node)
{}
public:
Iterator& operator++() {
node_ = node_->next_;
return *this;
}
bool operator==(Iterator b) const { return node_ == b.node_; }
bool operator!=(Iterator b) const { return node_ != b.node_; }
// Implement the rest of iterator interface.
};
template<class T>
class List {
class NodeT : public Node {
friend class List<T>;
T value_;
NodeT(T t) : value_(t) {}
};
template<class U>
class IteratorT : public Iterator {
friend class List<T>;
NodeT* node() const { return static_cast<NodeT*>(node_); }
public:
U& operator*() const { return node()->value_; }
U* operator->() const { return &node()->value_; }
operator IteratorT<U const>() const { return node_; } // iterator to const_iterator conversion
IteratorT(Node* node) : Iterator{node} {}
};
Node list_;
public:
using iterator = IteratorT<T>;
using const_iterator = IteratorT<T const>;
~List() { clear(); }
bool empty() const { return list_.next_ == &list_; }
iterator begin() { return list_.next_; }
iterator end() { return &list_; }
void push_back(T t) { list_.push_back(new NodeT(t)); }
void erase(const_iterator i) { delete i.node(); }
void clear() {
while(!empty())
erase(begin());
}
// Implement the rest of the functionality.
};
int main() {
List<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
for(auto elem : l)
std::cout << elem << ' ';
std::cout << '\n';
}
I'm trying to work on an assignment where the professor has requested that the iterator for a Linked List has to be a separate header file.
However, all implementations I've seen of Iterators I've seen have the definition of the class inside of the bag/list class. So I have no idea how to create an iterator within the bag like
dlist<int>::iterator it1
without defining it inside the list.
For reference, here are my list, node, and iterator classes as is:
dnode.h
template <class T>
class dnode{
public:
dnode(){
linknext = NULL;
prevlink = NULL;
}
void set_data(T item){data_value = item;}
void set_next(dnode<T> *link){linknext = link;}
void set_prev(dnode<T> *link){prevlink = link;}
T data()const{return data_value;}
dnode *next(){return linknext;}
dnode *prev(){return prevlink;}
private:
dnode *linknext;
dnode *prevlink;
T data_value;
};
dlist.h
#include "dnode.h"
#include "iterator.h"
template <class T>
class dlist{
public:
dlist(){head = tail = NULL;}
void rear_insert(T data);
void front_insert(T data);
void front_remove();
void rear_remove();
void reverse_show();
void show();
iterator<T> begin(){return iterator<T>(head);}
iterator<T> end(){return iterator<T>(tail);}
iterator<T> r_begin(){return iterator<T>(tail);}
iterator<T> r_end(){return iterator<T>(head);}
void insert_after(iterator<T>& current,T item){
dnode<T>* tmp;
tmp = new dnode<T>;
current.get_dnode()->next()->set_prev(tmp);
tmp->set_data(item);
tmp->set_next(current.get_dnode()->next());
tmp->set_prev(current.get_dnode());
current.get_dnode()->set_next(tmp);
}
void insert_before(iterator<T>& current,T item){
dnode<T>* tmp;
tmp = new dnode<T>;
current.get_dnode()->prev()->set_next(tmp);
tmp->set_data(item);
tmp->set_next(current.get_dnode());
tmp->set_prev(current.get_dnode()->prev());
current.get_dnode()->set_prev(tmp);
}
friend class iterator<T>;
private:
dnode<T> *head;
dnode<T> *tail;
};
iterator.h
template <class T>
class iterator
{
public:
iterator(dnode<T> *first = NULL){current = first;}
dnode<T>* get_dnode(){return current;}
T& operator *()const{return current->data();}
iterator& operator ++(){
current = current->next();
return *this;
}
iterator& operator ++(int){
iterator original(current);
current = current->next();
return original;
}
iterator& operator --(){
current = current->prev();
return *this;
}
iterator& operator --(int){
iterator original(current);
current = current->prev();
return original;
}
bool operator ==(const iterator something)const{ return current == something.current;}
bool operator !=(const iterator something)const{ return current == something.current;}
private:
dnode<T> *current;
};
Thanks for any assistance.
You can declare the iterator and node types within the list class but define them elsewhere. The syntax you need is of the form as follows:
template <class T>
class list {
public:
class iterator;
struct node;
node* head;
iterator begin()
{
return head;
}
iterator end()
{
return head->prev;
}
};
template <class T>
class list<T>::iterator {
node* pos;
public:
iterator(node* p) : pos (p) { }
// ...
};
template <class T>
struct list<T>::node {
node* next;
node* prev;
// ...
};
void f()
{
list<int> l;
list<int>::iterator i = l.begin();
list<int>::node n;
}
When inside the definition of list, node or iterator you should no longer need the list:: prefix. Outside (which includes return types on functions defined outside those classes) you do need the list:: prefix.
In this form your include structure will be the reverse of what you have above where list.h does not need to include node.h or iterator.h but both of those files will include list.h. You might want to include node.h and iterator.h at the end of list.h so that users of your class can get all the definitions by including list.h.
i am writing this code a generic list , now in this generic list i did a class for iterator that helds two parameters : one is a pointer to the list he points to .
and the other one points to the element in the list he points to ..
now i need to write an insert function that inserts a new element to a given list , with the following rules :: where i insert a new node to the list, and in this function if the iterator poiints to the end of the list the we add the new node to the end of the list else we insert the node one place before the node that the iterator currently points to , and if the iteratot points to a different node then thats an error .
now i want the iterator to be defoult so in case in the tests someone called the function with one parameter i want the iterator to be equal to the end element of the list.
the function end i wrote it inside of the list.
now in the insert function i did that but i get this error :
cannot call member function "List::iterator List::end()"without object
.
#include <iostream>
#include <assert.h>
#include "Exceptions.h"
template <class T>
class List {
public:
List();
List(const List&);
~List();
List<T>& operator=(const List& list);
template <class E>
class ListNode {
private:
ListNode(const E& t, ListNode<E> *next): data(new E(t)), next(next){}
~ListNode(){delete data;}
E* data;
ListNode<E> *next;
public:
friend class List<E>;
friend class Iterator;
E getData() const{
return *(this->data);
}
ListNode<E>* getNext() const{
return this->next;
}
};
class Iterator {
const List<T>* list;
int index;
ListNode<T>* current;
Iterator(const List<T>* list, int index): list(list),
index(index),current(NULL){
int cnt=index;
while (cnt > 0) {
current = current->next;
cnt--;
}
}
friend class List<T>;
friend class ListNode<T>;
public:
// more functions for iterator
};
Iterator begin() const ;
Iterator end() const;
void insert(const T& data, Iterator iterator=end());//here i get the error
void remove(Iterator iterator);
class Predicate{
private:
T target;
public:
Predicate(T i) : target(i) {}
bool operator()(const T& i) const {
return i == target;
}
};
Iterator find(const Predicate& predicate);
class Compare{
private:
T target;
public:
Compare(T i) : target(i) {}
bool operator()(const T& i) const {
return i < target;
}
};
bool empty() const;
int compareLinkedList(ListNode<T> *node1, ListNode<T> *node2);
private:
ListNode<T> *head;
ListNode<T> *tail;
int size;
};
any help would be amazing ! cause i don't know why such a mistake apears .
//insert function just in case :
template <typename T>
void List<T>::insert(const T& data, Iterator iterator=end()){
if(iterator.list!=this){
throw mtm::ListExceptions::ElementNotFound();
return;
}
ListNode<T> *newNode = new ListNode<T>(data, iterator.current);
if(iterator.index==size){
if (head == NULL) {
head = newNode;
tail=newNode;
}else{
Iterator temp(this,size-1);
temp.current->next=newNode;
}
//newNode->next=this->end().current;
}
else {
if (head == NULL) {
head = newNode;
tail=newNode;
}
else {
Iterator temp1(this,iterator.index-1);
temp1.current->next=newNode;
newNode->next=iterator.current->next;
}
}
size++;
}
void insert(const T& data, Iterator iterator=end());
This is the offending line. A default argument cannot be the result of a member function call. The right tool to achieve what you want is overloading.
void insert(const T& data, Iterator iterator);
void insert(const T& data) { insert(data, end()); }
Here we still defer to the insert function you implemented, but we call end from within the overloads body, where it's allowed.
Don't worry about the indirect call. This is a very small function that's defined inside the class declaration itself. Any decent compiler will inline it completely.
This is a learning project so please give any additional advice that comes to mind.
I am trying to learn data structures by re-implementing some STL containers/algorithms and I've started with linked lists. If I try to make a list of a class lacking a default constructor I would get a compiler error of "no appropriate default constructor":
#include "list.h"
#include <list>
class testA
{
private:
int mInt;
public:
testA(int i) : mInt(i) {}
};
int _tmain(int argc, _TCHAR* argv[])
{
std::list<testA> wS; // Fine
wS.push_back(1); // Fine
MTL::list<testA> wM; // 'testA' has no appropriate default constructor
wM.push_back(1);
return 0;
}
The problem is I'm using a dummy node in the list to store a link to the beginning and the end of the list. I use this mainly to get the .end() function to work properly and give a decrement-able iterator to one past the end of the list. When I declare an empty list one of the functions wants the default constructor for the templated type so it can make my dummy node! If no default constructor exists, it gives the error message.
On the positive side, it looks like a couple of STL implementations actually use the dummy head node idea. On the negative side, it looks like they pull some nasty tricks to get around this issue. My visual studio 2010 STL implementation eventually calls raw operator new and delete without calling the constructor. I've basically copied what it has (look for ::operator new and delete) and I get it to compile. Here is the full code:
#include <cassert>
#include <iostream>
namespace MTL
{
template< typename T >
class list
{
private:
// If ListNode is in the private part of list, clients
// can't mess around with ListNodes.
template <typename T>
class ListNode
{
private:
ListNode<T>* p_mNextNode;
ListNode<T>* p_mPreviousNode;
T mNodeVal;
public:
// ListNode() :
// p_mNextNode(0),
// p_mPreviousNode(0) {}
ListNode(T const & aVal) :
p_mNextNode(0),
p_mPreviousNode(0),
mNodeVal(aVal) {}
ListNode<T>* GetNextNode() {return p_mNextNode;}
ListNode<T>* GetPreviousNode() {return p_mPreviousNode;}
void SetNextNode(ListNode<T>* const aNode) {p_mNextNode = aNode;}
void SetPreviousNode(ListNode<T>* const aNode) {p_mPreviousNode = aNode;}
T& GetNodeVal() {return mNodeVal;}
};
public:
class iterator
{
private:
ListNode<T>* mIteratorNode;
public:
iterator() : mIteratorNode(0) {}
iterator(ListNode<T>* aListNode) {mIteratorNode = aListNode;}
T& operator*() {return mIteratorNode->GetNodeVal();}
iterator& operator++() {mIteratorNode = mIteratorNode->GetNextNode(); return *this;}
iterator& operator--() {mIteratorNode = mIteratorNode->GetPreviousNode(); return *this;}
bool operator==(iterator const& aIterator) {return mIteratorNode==aIterator.mIteratorNode;}
bool operator!=(iterator const& aIterator) {return !(mIteratorNode==aIterator.mIteratorNode);}
ListNode<T>* GetNode() {return mIteratorNode;}
ListNode<T>* GetNextNode() {return mIteratorNode->GetNextNode();}
ListNode<T>* GetPreviousNode() {return mIteratorNode->GetPreviousNode();}
};
private:
ListNode<T>* p_mHeadNode;
void insert(ListNode<T>* const aNewNode, iterator& aIterator)
{
ListNode<T>* currentNode = aIterator.GetNode();
ListNode<T>* currentsPreviousNode = currentNode->GetPreviousNode();
currentsPreviousNode->SetNextNode(aNewNode);
aNewNode->SetPreviousNode(currentsPreviousNode);
aNewNode->SetNextNode(currentNode);
currentNode->SetPreviousNode(aNewNode);
}
void eraseNode(ListNode<T>* aListNode)
{
ListNode<T>* previousNode = aListNode->GetPreviousNode();
ListNode<T>* nextNode = aListNode->GetNextNode();
previousNode->SetNextNode(aListNode->GetNextNode());
nextNode->SetPreviousNode(aListNode->GetPreviousNode());
if (p_mHeadNode != aListNode)
{
delete aListNode;
}
}
protected:
public:
list() : p_mHeadNode(static_cast<ListNode<T>*>(::operator new (sizeof(ListNode<T>))))
{
// To get .begin or .end to work immediately after construction
p_mHeadNode->SetNextNode(p_mHeadNode);
p_mHeadNode->SetPreviousNode(p_mHeadNode);
}
list(list const& aList) : p_mHeadNode(static_cast<ListNode<T>*>(::operator new (sizeof(ListNode<T>))))
{
p_mHeadNode->SetNextNode(p_mHeadNode);
p_mHeadNode->SetPreviousNode(p_mHeadNode);
ListNode<T>* pCurrent = (aList.p_mHeadNode)->GetNextNode();
while (pCurrent != aList.p_mHeadNode)
{
this->push_back(pCurrent);
pCurrent = pCurrent->GetNextNode();
}
}
void push_front(T const& aNewVal)
{
ListNode<T>* newNode = new ListNode<T>(aNewVal);
this->insert(newNode,this->begin());
}
void push_back(T const& aNewVal)
{
ListNode<T>* newNode = new ListNode<T>(aNewVal);
this->insert(newNode,this->end());
}
void push_back(ListNode<T>* const aListNode)
{
this->push_back(aListNode->GetNodeVal());
}
void push_front(ListNode<T>* const aListNode)
{
this->push_front(aListNode->GetNodeVal());
}
T& front(){ return p_mHeadNode->GetNextNode()->GetNodeVal(); }
T& back(){ return p_mHeadNode->GetPreviousNode()->GetNodeVal(); }
void pop_front() {this->eraseNode(p_mHeadNode->GetNextNode());}
void pop_back() {this->eraseNode(p_mHeadNode->GetPreviousNode());}
const T& front() const { return (p_mHeadNode->GetNextNode())->GetNodeVal(); }
const T& back() const { return (p_mHeadNode->GetPreviousNode())->GetNodeVal(); }
iterator begin() {return iterator(p_mHeadNode->GetNextNode());}
iterator end() {return iterator(p_mHeadNode);}
iterator insert(iterator aPosition, const T& aVal)
{
assert(0);
return iterator();
}
iterator insert(iterator aPosition, unsigned int n, const T& aVal)
{
ListNode<T>* newNode = 0;
for (unsigned int i = 0; i < n; ++i)
{
newNode = new ListNode<T>(aVal);
this->insert(newNode,aPosition);
++aPosition;
}
return iterator(newNode->GetNextNode());
}
iterator insert(iterator aPosition, iterator aFirst, iterator aLast)
{
assert(0);
return iterator();
}
unsigned int size()
{
unsigned int counter = 0;
ListNode<T>* pCurrent = p_mHeadNode->GetNextNode();
while (pCurrent != p_mHeadNode)
{
++counter;
pCurrent = pCurrent->GetNextNode();
}
return counter;
}
~list()
{
this->clear();
::operator delete(p_mHeadNode);
}
void clear()
{
ListNode<T>* pCurrent = p_mHeadNode->GetNextNode();
ListNode<T>* pNext = pCurrent->GetNextNode();
while (pNext != p_mHeadNode)
{
this->eraseNode(pCurrent);
pCurrent = pNext;
pNext = pCurrent->GetNextNode();
}
// All but the last has been deleted
this->eraseNode(pCurrent);
}
bool empty() {return (p_mHeadNode->GetNextNode() != p_mHeadNode);}
friend std::ostream& operator<<(std::ostream& os, list<T> const& aList)
{
ListNode<T>* pCurrent = (aList.p_mHeadNode)->GetNextNode();
std::cout << "List Contents are:\n";
std::cout << "{";
while (pCurrent != aList.p_mHeadNode)
{
std::cout << pCurrent->GetNodeVal();
pCurrent = pCurrent->GetNextNode();
if (pCurrent != aList.p_mHeadNode)
{
std::cout << ",";
}
}
std::cout << "}\n";
return os;
}
};
};
Surely, there must be a cleaner way to get this to work. I can't seem to split my ListNode into a base class of just previous and next pointers and a derived class of the contained data because GetNodeVal() needs a return type of the templated value. So again I would need at least an appropriate constructor to get a dummy value in the base class. I could not make this pure virtual because then my dummy node can not be instantiated as a base class.
This:
http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-3.3/stl__list_8h-source.html
version is using static casts which seem to be less nasty but I haven't been able to apply it to my code.
So, how can I get this code to work without calling raw operator new/deletes? And will it be any cleaner?
One option for delayed/optional construction of the value could be:
template <typename T>
class ListNode
{
private:
ListNode<T>* p_mNextNode;
ListNode<T>* p_mPreviousNode;
union {
char dummy;
T mNodeVal;
};
ListNode() :
p_mNextNode(0),
p_mPreviousNode(0),
dummy() {}
ListNode(T const & aVal) :
p_mNextNode(0),
p_mPreviousNode(0),
mNodeVal(aVal) {}
...
};
You will need to explicitly call its destructor, however, since the compiler no longer knows which of the variant members is active.
But it's better to do this for the dummy ListNode inside the List. I guess that the implementation with ::operator new was also special-casing the dummy. Something like this:
template< typename T >
class List
{
private:
class ListNode
{
private:
ListNode* p_mNextNode;
ListNode* p_mPreviousNode;
T mNodeVal;
};
class DummyListNode
{
public:
ListNode* p_mNextNode;
ListNode* p_mPreviousNode;
};
These are both "standard-layout", and by 9.2:
If a standard-layout union contains two or more standard-layout structs that share a common initial sequence, and if the standard-layout union object currently contains one of these standard-layout structs, it is permitted to inspect the common initial part of any of them.
Let's take advantage of that now:
union {
DummyListNode dummy_head_tail;
ListNode head_tail
};
// ... iterators and stuff, using &head_tail as the first and last ListNode*
public:
List() : dummy_head_tail{ nullptr, nullptr } { }
~List() { /* free all nodes, then */ dummy_head_tail->~DummyListNode(); }
};