I'm trying to write a class similar to std::list in C++.
It's my first time dealing with templated classes and I get this weird error:
C2447: '{' missing function header (old-style formal list?)
Here is the code snippet:
template<typename item>
mylist<item>::iterator mylist<item>::erase(iterator pos)
{
iterator cursor(head);
while (cursor->next != (*pos)) ++cursor;
if ((*cursor) == nullptr || (*cursor) == head) return iterator(nullptr);
m_node* tmp = cursor->next->next;
delete cursor->next;
cursor->next = tmp;
--m_size;
return tmp;
}
The class definition:
template<typename item>
class mylist
{
class m_node
{
friend class mylist;
item* node_data;
m_node* next;
public:
m_node()
{
next = nullptr;
node_data = nullptr;
}
m_node(const item& ITEM): m_node()
{
node_data = new item(ITEM);
}
~m_node() { delete node_data; delete next; }
};
m_node* head;
m_node* tail;
unsigned int m_size;
public:
class iterator
{
friend class mylist;
m_node* m_ptr;
public:
iterator()
{
m_ptr = nullptr;
}
iterator(m_node* i_ptr)
{
m_ptr = i_ptr;
}
iterator(const iterator& other)
{
m_ptr = other.m_ptr;
}
~iterator(){}
const iterator& operator=(const iterator& other) const
{
m_ptr = other.m_ptr;
return this;
}
iterator& operator++()
{
++m_ptr;
return *this;
}
item operator->()
{
return *(m_ptr->node_data);
}
item operator*()
{
return *(m_ptr->node_data);
}
bool operator!=(const iterator& other) { return m_ptr != other.m_ptr; }
bool operator ==(const iterator& other) { return m_ptr == other.m_ptr; }
};
mylist()
{
head = tail = new m_node();
m_size = 0;
}
~mylist()
{
delete head;
}
bool isempty() const
{
return head == tail;
}
const iterator& push_back(const item& i_item)
{
tail->next = new m_node(i_item);
tail = tail->next;
++m_size;
return iterator(tail);
}
iterator begin() const
{
return iterator(head);
}
iterator end() const
{
return nullptr;
}
item& back()
{
return *(tail->node_data);
}
unsigned int size() const
{
return m_size;
}
iterator erase(iterator pos);
void remove(item T);
};
The error occurs at the first curly of the function's scope.
I have read some documentations regarding this error and templated classes but could find what seems to be the error.
As mentioned in the comments, you will need a typename preceding the declared return type of the function template. This extra 'hint' for the compiler is required in this case because that return type is dependent on the item type; and, quoting from that linked cppreference page, it is "a compound type constructed from a dependent type".
template<typename item>
typename mylist<item>::iterator mylist<item>::erase(iterator pos)
{
iterator cursor{ head };
while (cursor->next != (*pos)) ++cursor;
if ((*cursor) == nullptr || (*cursor) == head) return iterator(nullptr);
m_node* tmp = cursor->next->next;
delete cursor->next;
cursor->next = tmp;
--m_size;
return tmp;
}
Also note that I have changed the parentheses to curly braces in the iterator cursor{ head }; line; this is now generally accepted as better style, to avoid possible confusion between object initializers and function declarations.
Related
template <typename T>
class LinkedList {
struct node;
class Iterator;
public:
LinkedList() {}
LinkedList(std::initializer_list<T> init_list) {
this->operator=(init_list);
}
template <typename InputIterator>
LinkedList(InputIterator first, InputIterator last) {
for (; first != last; ++first)
this->push_back(*first);
}
LinkedList(const LinkedList& another) {
this->operator=(another);
}
~LinkedList() {
while (this->head) {
node* old_head = this->head;
this->head = old_head->next;
delete old_head;
}
}
Iterator begin() {
return Iterator(this->head);
}
Iterator end() {
return Iterator(this->tail->next);
}
I tried to add an empty node at the tail->next, however I can't get the result that I want. And without the empty node I just get a segmentation fault when I run the code.
class Iterator {
friend class LinkedList;
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using difference_type = int;
using pointer = T*;
using reference = T&;
Iterator(node* ptr) : ptr(ptr) {}
Iterator(const Iterator& other) {
this->operator=(other);
}
Iterator& operator=(const Iterator& that) {
this->ptr = that.ptr;
return *this;
}
Iterator& operator++() {
this->ptr = ptr->next;
return *this;
}
Iterator operator++(int) {
Iterator tmp(*this);
this->operator++();
return tmp;
}
Iterator& operator--() {
this->ptr = ptr->prev;
return *this;
}
Iterator operator--(int) {
Iterator tmp(*this);
this->operator--();
return tmp;
}
bool operator!=(Iterator that) const { return !(this->operator==(that)); }
bool operator==(Iterator that) const { return this->ptr == that.ptr; }
T& operator*() const { return ptr->data; }
Iterator* operator->() { return this; }
private:
node* ptr = nullptr;
};
Here is the main function I used to test and when I print the stl_list end() it prints the size of the list. I'm confused what should be returned for this function. I thought it was supposed to be an empty nullptr that points to the location after the tail.
int main() {
std::list<int> stl_list{1, 2, 3, 4, 5};
cs19::LinkedList<int> our_list{1, 2, 3, 4, 5};
std::cout << *stl_list.begin() << '\n';
std::cout << *our_list.begin() << '\n';
std::cout << *our_list.end() << '\n';
std::cout << *stl_list.end() << '\n';
}
Confused on how to implement the end function for doubly linked list
You could create a special node type that is one past the end node which only has a prev pointer.
template <typename T>
class LinkedList {
struct empty_node { // used for end iterator
empty_node* prev = nullptr;
};
struct node : empty_node {
empty_node* next;
T data;
};
Your LinkedList would then have an instance of empty_node whos only purpose is to let prev point back to the last real node. You'd then instantiate the end() iterator with a pointer to this empty_node.
You'd then use empty_node* everywhere until stepping the iterator forward or dereferencing an iterator.
Example with comments to explain:
template <typename T>
class LinkedList {
// node definitions as above
public:
class Iterator {
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using difference_type = int;
using pointer = T*;
using reference = T&;
Iterator(empty_node* ptr) : ptr(ptr) {}
Iterator(const Iterator& other) : ptr(other.ptr) {}
Iterator& operator=(const Iterator& that) {
ptr = that.ptr;
return *this;
}
Iterator& operator++() {
// If the user steps forward, the iterator can't be at the end
// or the program will have undefined behavior as per the usual
// contract, so a cast is fine:
ptr = static_cast<node*>(ptr)->next;
return *this;
}
Iterator operator++(int) {
Iterator tmp(*this);
++*this;
return tmp;
}
Iterator& operator--() {
ptr = ptr->prev;
return *this;
}
Iterator operator--(int) {
Iterator tmp(*this);
--*this;
return tmp;
}
bool operator==(Iterator that) const { return this->ptr == that.ptr; }
bool operator!=(Iterator that) const {
return !(*this == that);
}
// Dereferencing is not allowed if the iterator is at the end so
// cast is fine:
reference operator*() const { return static_cast<node*>(ptr)->data; }
pointer operator->() { return &static_cast<node*>(ptr)->data; }
private:
empty_node* ptr = nullptr;
};
LinkedList() = default;
template <typename InputIterator>
LinkedList(InputIterator first, InputIterator last) {
for (; first != last; ++first) this->push_back(*first);
}
// Delegate to ctor taking iterators:
LinkedList(std::initializer_list<T> init_list)
: LinkedList(init_list.begin(), init_list.end()) {}
// Copy ctor - delegate to ctor taking iterators too:
LinkedList(const LinkedList& another)
: LinkedList(another.begin(), another.end()) {}
~LinkedList() {
// As long as it's not pointing at end, cast is fine:
for(empty_node* next; head != &beyond_end; head = next) {
next = static_cast<node*>(head)->next;
delete static_cast<node*>(head);
}
}
void push_back(const T& value) {
// Create a new node where `prev` points at the current last real node
// and `next` points at our empty end node:
node* nn = new node{{beyond_end.prev}, &beyond_end, value};
// link it:
if (head != &beyond_end) { // not the first node added
// the previous node must be a real node, so cast is fine:
static_cast<node*>(beyond_end.prev)->next = nn;
} else { // the first node added
head = nn;
}
beyond_end.prev = nn; // link beyond_end to the last real node
}
Iterator begin() { return Iterator(head); }
Iterator end() { return Iterator(&beyond_end); } // use `beyond_end` for end()
private:
empty_node* head = &beyond_end; // start pointing at the empty node
empty_node beyond_end; // note, not a pointer
};
So, instead of a node* tail; you'll have an instance of an empty_node in your LinkedList. It will have the same size as a node* so it doesn't waste space.
Demo
You could also store both the head and tail pointer in an empty_node to remove all casts except when dereferencing/deleteing.
template <typename T>
class LinkedList {
struct empty_node { // used for end iterator
empty_node* prev = nullptr;
empty_node* next = nullptr;
};
struct node : empty_node {
T data;
}
It requires minor changes to the example:
Demo
Consider a standard implementation of class Link of LinkedList in c++.
I want to know if its a good idea to overload the operator ++ in this class (I noticed that I repeat the line link = link->next; a lot when dealing with linked lists, so I thought it would be easier if I overload this operator). Here's my implementation:
#ifndef LINK_H
#define LINK_H
#include <iostream>
#include "typeinfo.h"
#include "LinkedList.h"
template <class T> class LinkedList; //|Forward declaration of the generic LinkedList.
template <class T>
class Link
{
public:
//|-------------------- Constructors --------------------
Link(T data): m_data(data), next(NULL){}
//|-------------------- Methods --------------------
T getData(){
return m_data;
}
T& getNext(){
return next;
}
void setNext(Link* newLink){
next = newLink;
}
void setData(T data){
m_data = data;
}
//|-------------------- Operator overload --------------------
bool operator==(Link& other){
if(this->m_data == other.m_data)
return true;
return false;
}
void operator++(){ //Is this okay?
this = this->next;
}
//|-------------------- Friend functions --------------------
friend std::ostream& operator<<(std::ostream& out,const Link<T>& link){
out<<link.m_data;
return out;
}
//|-------------------- Destructor --------------------
virtual ~Link(){}
protected:
public:
//|-------------------- Private fields --------------------
T m_data;
Link<T>* next;
friend class LinkedList<T>;
};
#endif // LINK_H
I guess the way that I tried to do it is not good (it does work as I expected). I tried to use this because I want it to work on pointer that is pointing to a certain link.
So, is it a good idea? if it is, what is the right way to implement it?
Thanks.
Maybe you should refactor your design and the code.
The link, or better said the Node, is normally implemented as an own class. An this class is embedded in the LinkedList class. And that Node class should be completely encapsulated and not shown to the outside world.
The user of the class will just deal with the data value. All the pointer stuff should be hidden.
And to be able to iterate over your class, which is similar to a std::forward_list, you can add ultra simple iterator functionality. Most functions can be implemented using a one liner.
I will show you below an ultra simple implementation example. This may be extended easily according to your needs.
From that you may take over some ideas to improve your design.
With the added iterator functionality, you may use range based for loops and std:: algorithms and all the like.
Please have a look and ask questions, if there should be something unclear.
#include <iostream>
#include <iterator>
#include <initializer_list>
#include <algorithm>
// Very simple implementation of a forward list
template <class T>
class SinglyLinkedList {
// The node
struct Node {
T data{}; // Data. Would normally be a templated argument
Node* next{}; // And the pointer to the next node
Node(const T& i, Node* n = nullptr) : data(i), next(n) {}; // Simple constructor to set a value and next pointer
};
Node* head{}; // This is the start of the list
// It would be advisable to have a tail pointer. We use the more inefficient approach here
Node* getLast() const { Node* n{ head }; while (n and n->next) n = n->next; return n; }
public:
// Constructor / Destructor --------------------------------------------------------------------------------------------------------
~SinglyLinkedList() { clear(); }
// Default constuctor
SinglyLinkedList() {} // Default
// From an initialization list
SinglyLinkedList(const std::initializer_list<T>& il) { clear(); for (const T& i : il) push_back(i); } // From initializer list
// Copy constructor
SinglyLinkedList(const SinglyLinkedList& other) { clear(); for (const T &i : other) push_back(i); }
// Move constructor. Will steal the elements from the other
SinglyLinkedList(SinglyLinkedList&& other) noexcept { head = other.head; other.head = nullptr; }
// Assignment operator
SinglyLinkedList& operator = (const SinglyLinkedList& other) { clear(); for (const T &i : other) push_back(i); }
// Move assignment operator
SinglyLinkedList& operator = (SinglyLinkedList&& other) { head = other.head; other.head = nullptr; }
// Housekeeping --------------------------------------------------------------------------------------------------------------
void clear() { Node* tmp{ head }; while (tmp) { Node* toDelete{ tmp }; tmp = tmp->next; delete toDelete; } head = nullptr; }
int empty() const { return head == nullptr; }
int size() const { int k{}; Node* n{ head }; while (n) { ++k; n = n->next; } return k; }
// Modify content --------------------------------------------------------------------------------------------------------------
void push_front(const T& i) { Node* n = new Node(i); n->next = head; head = n; };
void push_back(const T& i) { Node* n = new Node(i); Node* l = getLast(); if (l) l->next = n; else head = n; }
void pop_front() { if (head) { Node* tmp = head->next; delete head; head = tmp; } }
void pop_back() { // This is a little bit more difficult in a singly linked list
if (head) {
Node* n{ head }, * previous{};
while (n and n->next) {
previous = n;
n = n->next;
}
delete n;
if (previous)
previous->next = nullptr;
else
head->next = nullptr;
}
}
// Access elements --------------------------------------------------------------------------------
T front() const { return head ? head->data : 0; };
T back() const { Node* n = getLast(); return n ? n->data : 0; }
// Add iterator properties to class ---------------------------------------------------------------
struct iterator { // Local class for iterator
Node* iter{}; // Iterator is basically a pointer to the node
// Define alias names necessary for the iterator functionality
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
// Constructor
iterator() {}
iterator(Node* n) : iter(n) {}
// Dereferencing
reference operator *() const { return iter->data; }
pointer operator ->() const { return &iter->data; }
// Aithmetic operations
iterator& operator ++() { if (iter) iter = iter->next; return *this; }
iterator operator ++(int) { iterator temp{ *this }; ++* this; return temp; }
iterator operator +(const difference_type& n) const { iterator temp{ *this }; difference_type k{ n }; while (k--)++temp; return temp; }
iterator& operator +=(const difference_type& n) { difference_type k{ n }; while (k--)++* this; return *this; };
// Comparison
bool operator != (const iterator& other) const { return iter != other.iter; }
bool operator == (const iterator& other) const { return iter == other.iter; }
bool operator < (const iterator& other) const { return iter < other.iter; }
bool operator > (const iterator& other) const { return iter > other.iter; }
bool operator <= (const iterator& other) const { return iter <= other.iter; }
bool operator >= (const iterator& other) const { return iter >= other.iter; }
// Difference. Also complicated, because no random access
difference_type operator-(const iterator& other) const {
difference_type result{};
Node* n{ iter };
while (n and n != other.iter) {
++result;
n = n->next;
}
return result;
}
};
// Begin and end function to initialize an iterator
iterator begin() const { return iterator(head); }
iterator end() const { return iterator(); }
// Functions typcical for forward lists ----------------------------------------------------------------------
// Easy, becuase we can operate form the current iterator and do not need the "previous" element
iterator insertAfter(iterator& pos, const T& i) {
iterator result{};
if (pos.iter and pos.iter->next) {
Node* n = new Node(i, pos.iter->next);
pos.iter->next = n;
result = n;
}
return result;
}
iterator eraseAfter(iterator& pos) {
iterator result{};
if (pos.iter and pos.iter->next) {
Node* tmp = pos.iter->next->next;
delete pos.iter->next;
pos.iter->next = tmp;
result = pos.iter->next;
}
return result;
}
};
// Test/Driver Code
int main() {
// Example for initilizer list
SinglyLinkedList<int> sllbase{ 5,6,7,8,9,10,11,12,13,14,15 };
// Show move constructor
SinglyLinkedList<int> sll(std::move(sllbase));
// Add some values in the front
sll.push_front(4);
sll.push_front(3);
sll.push_front(2);
sll.push_front(1);
// Delete 1st element (Number 1)
sll.pop_front();
// Delete last element
sll.pop_back();
// Use a std::algorithm on our custom linked list. Works because we have an interator
SinglyLinkedList<int>::iterator iter = std::find(sll.begin(), sll.end(), 8);
// Now add an element after 8
iter = sll.insertAfter(iter, 88);
// End delete the 9
iter = sll.eraseAfter(iter);
// Use range based for loop. Works because, we have iterators
for (int i : sll)
std::cout << i << ' ';
}
I need to implement following function:
int size() const;
function returns number of pieces of data stored in the list
Time complexity - O(1).
Basically I have a class called DList contained in DList.h which has the following structure:
class DList
{
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 *front_;
Node *back_;
public:
class const_iterator
{
friend class DList;
protected:
Node *curr_;
//assign curr_ with n;
const_iterator(Node *n)
{
curr_ = n;
}
public:
const_iterator()
{
curr_ = nullptr;
}
const_iterator operator++()
{
//++x
curr_ = curr_->next_;
return *this;
};
const_iterator operator++(int)
{
//x++
const_iterator old = *this;
curr_ = curr_->next_;
return old;
};
const_iterator operator--()
{
curr_ = curr_->prev_;
return *this;
}
const_iterator operator--(int)
{
//x--
const_iterator old = *this;
curr_ = curr_->prev_;
return old;
}
bool operator==(const_iterator rhs)
{
return (curr_ == rhs.curr_);
}
bool operator!=(const_iterator rhs)
{
return (curr_ != rhs.curr_);
}
const T &operator*() const
{
return curr_->data_;
}
};
class iterator : public const_iterator
{
friend class DList;
iterator(Node *n) : const_iterator(n) {}
public:
iterator() {}
iterator operator++()
{
//++x
this->curr_ = this->curr_->next_;
return *this;
}
iterator operator++(int)
{
//x++
iterator old = *this;
this->curr_ = this->curr_->next_;
return old;
}
iterator operator--()
{
//--x
this->curr_ = this->curr_->prev_;
return *this;
}
iterator operator--(int)
{
//x--
iterator old = *this;
this->curr_ = this->curr_->prev_;
return old;
}
T &operator*()
{
return this->curr_->data_;
}
};
DList();
~DList();
DList(const DList &rhs);
DList &operator=(const DList &rhs);
DList(DList &&rhs);
DList &operator=(DList &&rhs);
iterator begin()
{
return iterator(front_);
}
iterator end()
{
return iterator(nullptr);
}
const_iterator begin() const
{
return const_iterator(front_);
}
const_iterator end() const
{
return const_iterator(nullptr);
}
void push_front(const T &data);
void push_back(const T &data);
void pop_front();
void pop_back();
iterator insert(const T &data, iterator it);
iterator search(const T &data);
const_iterator search(const T &data) const;
iterator erase(iterator it);
iterator erase(iterator first, iterator last);
bool empty() const;
int size() const; <--------- THIS IS THE FUNCTION I NEED TO IMPLEMENT
};
The function size() is being called in the tester program main.cpp as following:
DList<Record> recList;
DList<int> intList;
.
.
.
.
if (!checkList(recList, mirror, 15) || recList.empty() || recList.size() != 15)
{
passtest = false;
cout << "Error 9a: Bug in = operator, no deepcopy?" << endl;
}
For O(1) complexity, you need to keep a running count that is updated whenever you add or remove a node. The default constructor must initialize it to zero, and other constructors and assignments must do the right thing (DTRT).
class DList
{
struct Node;
Node *front_;
Node *back_;
size_t count_;
public:
Dlist()
: front_(nullptr)
, back_(nullptr)
, count_(0)
{}
size_t size() const { return count_; }
// etc..
}
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'm using IAR as compiler for embedded project. I'm trying to introduce some templates for basic types like list, but each STL list object created increases code size by about 200 bytes relatively to our current C style implementation.
I tried to implement a small portion of the STL list myself hoping to get a smaller code footprint, but ended up being more heavy than the full STL list.
Am I doing something horribly wrong in my usage of templates?
Thanks
P.S. Please note that the code is untested so it may contain dragons.
#ifndef __LINK_LIST_HPP__
#define __LINK_LIST_HPP__
#include <stdint.h>
#include <stdlib.h>
template <typename T> class list
{
private:
struct LinkListElement
{
T payload;
LinkListElement* next;
LinkListElement* prev;
};
public:
class iterator
{
// Need access to LinkListElement struct
friend class list;
public:
iterator() : m_cur_item(NULL){}
iterator(LinkListElement* elem) : m_cur_item(elem){}
iterator(const iterator& other) : m_cur_item(other.m_cur_item){}
~iterator(){}
iterator& operator=(const iterator& other)
{
m_cur_item = other.m_cur_item;
return *this;
}
bool operator==(const iterator& other) const
{
// Compare by position, ignoring the payload contents when comparing iterators.
return (m_cur_item->next == other.m_cur_item->next) &&
(m_cur_item->prev == other.m_cur_item->prev);
}
bool operator!=(const iterator& other) const
{
return !(*this == other);
}
// Prefix increment operator.
iterator& operator++()
{
increment();
return *this;
}
// Postfix increment operator.
iterator operator++(int)
{
iterator copy(*this);
increment();
return copy;
}
// Prefix decrement operator.
iterator& operator--()
{
decrement();
return *this;
}
// Postfix decrement operator.
iterator operator--(int)
{
iterator copy(*this);
decrement();
return copy;
}
T& operator*()
{
// Just so we won't crash, but behavior is undefined.
if (m_cur_item == NULL)
{
return dummy;
}
return m_cur_item->payload;
}
T* operator->()
{
if (m_cur_item == NULL)
{
return NULL;
}
return &(m_cur_item->payload);
}
private:
void increment()
{
if (m_cur_item == NULL || m_cur_item->next == NULL)
{
return;
}
m_cur_item = m_cur_item->next;
}
void decrement()
{
if (m_cur_item == NULL || m_cur_item->prev == NULL)
{
return;
}
m_cur_item = m_cur_item->prev;
}
LinkListElement* m_cur_item;
static T dummy;
};
// Need access to internal LinkListElement pointer
friend class iterator;
list()
{
// Add sentinel to mark end of list.
m_tail = new LinkListElement;
m_tail->next = m_tail;
m_tail->prev = m_tail;
m_head = m_tail;
}
~list()
{
// Clear entire list except for sentinel
clear();
// Destroy sentinel
delete m_tail;
m_head = NULL;
m_tail = NULL;
}
T& back()
{
// empty list with only sentinel. Result of back() is undefined
if (empty())
{
// TODO: Show some debug error
}
return m_tail->prev->payload;
}
T& front()
{
if (empty())
{
// TODO: Show some debug error
}
// m_head is always defined even if list is empty
return m_head->payload;
}
size_t size()
{
return m_count;
}
bool empty()
{
// head == tail means the list is empty
return m_head == m_tail;
}
iterator begin()
{
return iterator(m_head);
}
iterator end()
{
return iterator(m_tail);
}
iterator insert(iterator position, const T& payload)
{
// Validate position by finding it in our list
iterator find = begin();
while (find != end() && find != position)
{
++find;
}
if (find == end())
{
// TODO: Show some debug error
return position;
}
return insert_before(find.m_cur_item, payload);
}
void push_back(const T& payload)
{
insert_before(m_tail, payload);
}
void push_front(const T& payload)
{
insert_before(m_head, payload);
}
iterator erase(iterator position)
{
// Validate position by finding it in our list
iterator find = begin();
while (find != end() && find != position)
{
++find;
}
if (find == end())
{
// TODO: Show some debug error
return position;
}
return remove_at(find.m_cur_item);
}
//iterator erase(iterator first, iterator last); // Implement only if needed
void pop_back()
{
if (!empty())
{
// Don't remove the sentinel
remove_at(m_tail->prev);
}
}
void pop_front()
{
if (!empty())
{
remove_at(m_head);
}
}
void remove(const T& value)
{
iterator iter = begin();
while (iter != end())
{
iterator remove = iter++;
if (*remove == value)
{
remove_at(remove.m_cur_item);
}
}
}
void clear()
{
while (!empty())
{
pop_back();
}
}
private:
iterator insert_before(LinkListElement* existing, const T& payload)
{
// Allocate memory and save the element
LinkListElement* new_elem = new LinkListElement;
// For classes types (not pointer to object) this should invoke copy constructor
new_elem->payload = payload;
new_elem->prev = existing->prev;
new_elem->next = existing;
existing->prev = new_elem;
++m_count;
if (existing == m_head)
{
m_head = new_elem;
}
return iterator(new_elem);
}
iterator remove_at(LinkListElement* to_remove)
{
// Allocate memory and save the element
LinkListElement* prev = to_remove->prev;
LinkListElement* next = to_remove->next;
prev->next = next;
next->prev = prev;
--m_count;
if (to_remove == m_head)
{
m_head = next;
}
delete to_remove;
return iterator(prev);
}
LinkListElement* m_head;
LinkListElement* m_tail;
uint32_t m_count;
};
template <typename T> T list<T>::iterator::dummy;
#endif
You code has all kinds of features and checks that the STL doesn't have. So it makes sense that you would wind up with more code.
Yes, the STL has lots of features your code doesn't have. But you aren't using any of them, so they don't show up in your code footprint. The STL is designed using templates so that you don't pay for what you don't use.
It's unlikely you can improve on the STL. If you need to add features, add them. You don't need to reinvent the wheel.