I am trying to implement an iterator for a binary search tree. I was asked not to use any STL in my iterator. I only have to override the operators: ++, operator*, and !=. I get an error with the operator*: "no *operator matches this operands. Operands types are *iterator<std::string>". I'm using a template library, so I'm not sure why it's not working.
Here's my code:
template <typename T>
class Iterator : public std::iterator<std::forward_iterator_tag, {
public:
Iterator(TreeNode<T>* root)
{
this->current = root;
}
template <typename T>
bool operator!=(Iterator<T> const & other) const
{
return this->current != other.current;
}
template <typename T>
T &operator*() const {
return current->element;
}
Iterator operator++()
{
current = current->nextInorder();
return *this;
}
Iterator operator++(int dummy)
{
TreeNode<T> temp = current;
current = current->nextInorder();
return *temp;
}
private:
TreeNode<T>* current;
void nextInorder()
{
if (current->element == NULL)return;
else {
nextInorder(current->left);
nextInorder(current->element);
nextInorder(current->right);
}
}
};
The code is not well-pasted (see the class Iterator...line).
I would recommend removing template <typename T> on bool operator!=(Iterator<T> const & other) const and T &operator*() const methods. Because the T is the one used for the class instantiation.
Related
I have an assignment in which I need to make template classes LinkedList and Traversible. Class Traversible needs to be a interface which declares functions for indexing and iteration of some collection class. I don't exactly know how to make an interface for iterator so LinkedList can use it. I was thinking something like
template <class T, class U>
class ITraversible {
public:
virtual U begin() noexcept = 0;
virtual U end() noexcept = 0;
virtual T& operator[](int) = 0;
};
and then in LinkedList header file I would do:
template <class T>
class LinkedList : public ITraversible<T,typename LinkedList<T>::iterator> {
struct node {
T data;
node* next, *prev;
explicit node(const T&);
void connect(node*);
};
node *head, *tail;
int n;
public:
/*************************ITERATOR************************/
class iterator : public std::iterator<std::bidirectional_iterator_tag, node*> {
typename LinkedList<T>::node* itr;
explicit iterator(node*) noexcept;
friend class LinkedList;
public:
iterator& operator++();
iterator operator++(int);
iterator& operator--();
iterator operator--(int);
bool operator==(const iterator&) const noexcept;
bool operator!=(const iterator&) const noexcept;
T& operator*() const noexcept;
T& operator->() const noexcept;
};
/**********************************************************/
LinkedList() noexcept;
LinkedList(std::initializer_list<T>);
LinkedList(const LinkedList&);
LinkedList(LinkedList&&) noexcept;
~LinkedList() noexcept;
LinkedList& operator=(LinkedList) noexcept;
template <class A>
friend void swap(LinkedList<A>&, LinkedList<A>&);
void add(const T&);
void removeAt(int);
int size() const noexcept;
bool operator==(const LinkedList&) const noexcept;
bool operator!=(const LinkedList&) const noexcept;
virtual T& operator[](int) override;
virtual iterator begin() noexcept override;
virtual iterator end() noexcept override;
};
But then Traversable template has two parameters and it should have only one.
Is this what I am supposed to do? Keep in mind I am new to templates and iterators.
When creating an interface you'll need to nail down the static types of what is being returned. These may behave dynamically different but you can't change the type other than using a subtype relation when returning pointers or references.
Personally, I think this exercise is ill-advised for a C++ context. It may make some sense when using Java or C#. However, similar behavior can be obtained. A rought sketch would be something like this (although this should work it will be rather slow):
template <typename T>
struct iterator_base {
virtual iterator_base() {}
virtual iterator_base<T>* do_clone() = 0;
virtual T& do_value() = 0;
virtual void do_next() = 0;
virtual bool do_equal() = 0;
// other operations to implement operator--, operator[], ...
};
template <typename It>
class iterator: iterator_base<typename std::iterator_traits<It>::value_type> {
typedef typename std::iterator_traits<It>::value_type> type;
It it;
iterator_base<type>* do_clone() { return new iterator<It>(*this); }
type& do_value() { return *this->it; }
void do_next() { ++this->it; }
bool do_equal(iterator_base<type>* other) {
return this->it == static_cast<iterator<It>>(other)->it;
}
};
template <typename T>
class poly_iterator {
std::unique_ptr<iterator_base<T>> ptr;
public:
poly_iterator(iterator_base<T>* ptr): ptr(ptr) {}
poly_iterator(poly_iterator const& other): ptr(other.ptr->clone()) {}
poly_iterator& operator= (poly_iterator other) {
other.swap(this);
return *this;
}
void swap(poly_iterator& other) { swap(this->ptr, other.ptr); }
T& operator*() { return this->ptr->value(); }
T* operator->() { return &this->operator*(); }
poly_iterator& operator++() { this->ptr->next(); return *this; }
poly_iterator operator++(int) {
poly_iterator rc(*this);
this->operator++();
return rc;
}
bool operator== (poly_iterator const& other) {
return this->ptr->equal(other.ptr.ptr());
}
bool operator!= (poly_iterator const& other) {
return !(*this == other);
}
// other operations
};
// define a suitable specialization of std::iterator_traits<poly_iterator<T>>
template <typename T>
class ITraversible {
virtual iterator_base<T>* do_begin() = 0;
virutal iterator_base<T>* do_end() = 0;
public:
poly_iterator<T> begin() { return this->do_begin(); }
poly_iterator<T> end() { return this->do_end(); }
// other operations
};
template <typename T>
class List: public ITraversible<T> {
std::list<T> list;
iterator_base<T>* do_begin() {
return iterator<std::list<T>::iterator>(list.begin());
}
iterator_base<T>* do_end() {
return iterator<std::list<T>::iterator>(list.end());
}
public:
// whatever is needed to fill the list
};
I'm trying to define an internal list as template class that has a type safe container_of member function. For that the template must include the type of the container and the offset where in the container the list can be found (a member pointer). (See below for an example in C).
It should be something like this:
template <class T, List * T::*MEMBER> class List { ... }
But in the <> the type List is not yet defined so it can't be used. My next try was:
template <class T, class L, L * T::*MEMBER> class List { ... };
class Container {
List<Container, List<???>, Container::list> list;
};
But what to put for the "???"? That would have to be the whole <>, including the ???. So you get an endless recursion.
Next I tried to cheat a bit on the type safety:
template <class T, void * T::*M>
class List {
public:
T * container_of() {
return (T *)(intptr_t(this) - intptr_t(&((T *)NULL)->M)); \
}
};
class Container {
public:
List<Container, Container::item1> item1;
};
But that gives me:
error: incomplete type 'Container' used in nested name specifier
List<Container, Container::item1> item1;
^
Using C preprocessor makros what I want looks like this:
#include <unistd.h> // for NULL
#include <stdint.h> // for intptr_t
#include <iostream>
#define LIST(TYPE, MEMBER) \
class List_ ## MEMBER ## _t { \
public: \
TYPE * container_of() { \
return (TYPE *)(intptr_t(this) - intptr_t(&((TYPE *)NULL)->MEMBER)); \
} \
} MEMBER
class Container {
public:
LIST(Container, item1);
LIST(Container, item2);
};
int main() {
Container c;
std::cout << "Container at " << &c << std::endl;
std::cout << "Container of item1 = " << c.item1.container_of() << std::endl;
std::cout << "Container of item2 = " << c.item2.container_of() << std::endl;
}
So can this be expressed with templates at all?
I found a solution. It's not 100% perfect but close.
The idea is to have 3 classes:
class Item;
template <class T, Item T::*M> class Iterator;
template <class T, Item T::*M> class Head;
The Item class contains the next/prev links that form the actual list in memory. This doesn't include the container type and position of the list inside the container and (on its own) is unsafe. But an Item has no method to modify the list. All modifications are done through the Iterator. Even construction is done using a Head to get an Iterator and that to initialize the next/prev pointers.
The Iterator class can be constructed from a container T and has operator ++, --, == and !=, can insert a container into the current position or move the container behind another iterator to its own list. The Iterator also has operator * which returns the current container and operator bool to say if the end of the list has been reached.
The Head class contains a special head and tail Item with prev==NULL and next==NULL respectively. They are special since they aren't inside an instance of container T and mark the begining and end of the list. Other than holding the end markers the Head provides methods to create Iterators pointing at the head, tail, first and last element. This allows iterating the list or inserting at the start or end.
There is a 4th class ConstIterator that is like Iterator but for const access.
Note: This is only minimally tested. The remaining errors are left for the reader to fix.
class Item;
template <class T, Item T::*M> class Iterator;
template <class T, Item T::*M> class ConstIterator;
template <class T, Item T::*M> class Head;
template<class T, Item T::*M>
T * container_of(Item *item) {
return (T *)(intptr_t(item) - intptr_t(&(((T *)NULL)->*M)));
}
template<class T, Item T::*M>
const T * container_of(const Item *item) {
return (const T *)(intptr_t(item) - intptr_t(&(((const T *)NULL)->*M)));
}
class Item {
public:
template <class T, Item T::*M> Item(Head<T, M> *head, T *container) {
assert((container_of<T, M>(this)) == container);
head->tail().insert_before(container);
}
~Item() {
if (next_) next_->prev_ = prev_;
if (prev_) prev_->next_ = next_;
next_ = NULL;
prev_ = NULL;
}
private:
template <class T, Item T::*M> friend class Iterator;
template <class T, Item T::*M> friend class ConstIterator;
template <class T, Item T::*M> friend class Head;
Item(Item *next__, Item *prev__) : next_(next__), prev_(prev__) { }
Item(const Item &) = delete;
Item & operator =(const Item &) = delete;
Item *next_;
Item *prev_;
};
template <class T, Item T::*M>
class Iterator {
public:
Iterator() : item_(NULL) { }
Iterator(T *container) : item_(&(container->*M)) { }
~Iterator() { }
operator bool() const {
assert(item_);
// not head and not tail
return ((item_->next_ != NULL) && (item_->prev_ != NULL));
}
T & operator *() {
assert(item_);
if ((item_->next_ == NULL) || (item_->prev_ == NULL)) {
// head or tail has no container
assert(false);
}
return *container_of<T, M>(item_);
}
T & operator ->() {
assert(item_);
if ((item_->next_ == NULL) || (item_->prev_ == NULL)) {
// head or tail has no container
assert(false);
}
return *container_of<T, M>(item_);
}
Iterator & operator ++() {
assert(item_);
assert(item_->next_);
item_ = item_->next_;
return *this;
}
Iterator & operator --() {
assert(item_);
assert(item_->prev_);
item_ = item_->prev_;
return *this;
}
bool operator ==(const Iterator &other) {
assert(item_);
return (item_ == other.item_);
}
bool operator !=(const Iterator &other) {
assert(item_);
return (item_ != other.item_);
}
void move_before(Iterator &from) {
assert(item_);
assert(from);
assert(item_->prev_);
Item *before = item_->prev_;
Item *after = item_;
Item *item = from.item_;
// remove from old list
item->next_->prev_ = item->prev_;
item->prev_->next_ = item->next_;
// insert into this list
item->next_ = after;
item->prev_ = before;
before->next_ = item;
after->prev_ = item;
}
void insert_before(T *container) {
assert(item_);
assert(item_->prev_);
Item *before = item_->prev_;
Item *after = item_;
Item *item = &(container->*M);
// insert into this list
item->next_ = after;
item->prev_ = before;
before->next_ = item;
after->prev_ = item;
}
private:
Item *item_;
};
template <class T, Item T::*M>
class ConstIterator {
public:
ConstIterator() : item_(NULL) { }
ConstIterator(const T *container) : item_(&(container->*M)) { }
~ConstIterator() { }
operator bool() const {
assert(item_);
// not head and not tail
return ((item_->next_ != NULL) && (item_->prev_ != NULL));
}
const T & operator *() const {
assert(item_);
if ((item_->next_ == NULL) || (item_->prev_ == NULL)) {
// head or tail has no container
assert(false);
}
return *container_of<T, M>(item_);
}
const T & operator ->() const {
assert(item_);
if ((item_->next_ == NULL) || (item_->prev_ == NULL)) {
// head or tail has no container
assert(false);
}
return *container_of<T, M>(item_);
}
ConstIterator & operator ++() {
assert(item_);
assert(item_->next_);
item_ = item_->next_;
return *this;
}
ConstIterator & operator --() {
assert(item_);
assert(item_->prev_);
item_ = item_->prev_;
return *this;
}
bool operator ==(const ConstIterator &other) const {
assert(item_);
return (item_ == other.item_);
}
bool operator !=(const ConstIterator &other) {
assert(item_);
return (item_ != other.item_);
}
private:
const Item *item_;
};
template <class T, Item T::*M>
class Head {
public:
Head() : head_(&tail_, NULL), tail_(NULL, &head_) { }
~Head() { }
Iterator<T, M> head() {
return Iterator<T, M>(container_of<T, M>(&head_));
}
ConstIterator<T, M> head() const {
return ConstIterator<T, M>(container_of<T, M>(&head_));
}
Iterator<T, M> tail() {
return Iterator<T, M>(container_of<T, M>(&tail_));
}
ConstIterator<T, M> tail() const {
return ConstIterator<T, M>(container_of<T, M>(&tail_));
}
Iterator<T, M> first() {
return Iterator<T, M>(container_of<T, M>(head_.next_));
}
ConstIterator<T, M> first() const {
return ConstIterator<T, M>(container_of<T, M>(head_.next_));
}
Iterator<T, M> last() {
return Iterator<T, M>(container_of<T, M>(tail_.prev_));
}
ConstIterator<T, M> last() const {
return ConstIterator<T, M>(container_of<T, M>(tail_.prev_));
}
bool is_empty() const {
return (first() == tail());
}
private:
Head(const Head &) = delete;
Head & operator =(const Head &) = delete;
Item head_;
Item tail_;
};
I'm trying to write an iterator that iterates on multiple (sorted) lists.
I have one that works but I'd like to improve it.
Here's what I have for now.
#ifndef __multi_iterator__
#define __multi_iterator__
#include <list>
template <typename T>
class multi_iterator
{
private:
typedef typename std::list<T>::const_iterator iterator;
typedef std::list<iterator> iterator_list;
public:
multi_iterator();
multi_iterator(const multi_iterator<T>& other);
multi_iterator& operator = (const multi_iterator<T>& other);
virtual ~multi_iterator();
void add_list(const std::list<T>& it);
const T& operator * ();
multi_iterator<T>& operator ++ ();
multi_iterator<T> operator ++ (int unused);
bool operator == (const multi_iterator<T>& other);
bool operator != (const multi_iterator<T>& other);
protected:
iterator_list _it_list;
iterator_list _end_list;
private:
iterator& next();
};
template <typename T>
multi_iterator<T>::multi_iterator()
: _it_list()
{
}
template <typename T>
multi_iterator<T>::multi_iterator(const multi_iterator<T>& other)
: _it_list(other._it_list)
{
}
template <typename T>
multi_iterator<T>& multi_iterator<T>::operator = (const multi_iterator<T>& other)
{
_it_list = other._it_list;
}
template <typename T>
multi_iterator<T>::~multi_iterator<T>()
{
}
template <typename T>
void multi_iterator<T>::add_list(const std::list<T>& l)
{
_it_list.push_back(l.begin());
_end_list.push_back(l.end());
}
template <typename T>
const T& multi_iterator<T>::operator * ()
{
return *(next());
}
template <typename T>
multi_iterator<T>& multi_iterator<T>::operator ++ ()
{
++(next());
return *this;
}
template <typename T>
typename multi_iterator<T>::iterator& multi_iterator<T>::next()
{
typename iterator_list::iterator it = _it_list.begin();
typename iterator_list::iterator end_it = _end_list.begin();
typename iterator_list::iterator cur_it = _it_list.end();
for (; it != _it_list.end(); ++it)
{
if (*it != *end_it)
{
if ((cur_it == _it_list.end()) || (**it < **cur_it))
{
cur_it = it;
}
}
++end_it;
}
return *cur_it;
}
template <typename T>
multi_iterator<T> multi_iterator<T>::operator ++ (int unused)
{
return ++(*this);
}
template <typename T>
bool multi_iterator<T>::operator == (const multi_iterator<T>& other)
{
return _it_list == other._it_list;
}
template <typename T>
bool multi_iterator<T>::operator != (const multi_iterator<T>& other)
{
return !(*this == other);
}
#endif /* defined(__multi_iterator__) */
Here are the questions I've been pondering:
What should a C++ iterator do when it reaches the end (trying to look like stdlib). Throw an exception?
I don't think my keeping the list of iterator "ends" is elegant. Neither is my way to find if all iterators are at the end in next(). Does anyone have a cleaner solution?
Currently next() runs in linear time and is called for both * and ++ operators. I'm thinking I could save the current iterator and get the * operator to run in constant time. Also, If I sort my list each time I call ++, would ++ run in nlog(n)? I heard that this can be done in log(n) time and I can't really find a way to do that. What are your thoughts on complexity and optimization for this?
What you're trying to do is pretty well covered by zip iterators -- the Boost.Iterator library provides one. Check out their implementation.
You should also check out this discussion if you need to be able to add containers dynamically:
Zip Several Iterators in C++
What should a C++ iterator do when it reaches the end (trying to look like stdlib). Throw an exception?
It should become singular; that is, it must remain able to be compared with other iterators from the same sequence, but does not need to be dereferencable. Specifically, it must compare equal to another past-the-end iterator, and not equal to any non-singular iterator.
It certainly shouldn't throw an exception.
I'm a very confused student. Any help you can give is appreciated. I'm unsure how to write this code for a template class. I keep getting some odd compiling errors that look like this:
g++ -c test_list.cpp
List.cpp: In constructor ‘cop4530::List<T>::iterator::iterator(cop4530::List<T>::Node*) [with T = int]’:
List.cpp:247: instantiated from ‘typename cop4530::List<T>::iterator cop4530::List<T>::begin() [with T = int]’
test_list.cpp:25: instantiated from here
List.cpp:112: error: invalid conversion from ‘cop4530::List<int>::Node*’ to ‘int’
List.cpp:112: error: initializing argument 1 of ‘cop4530::List<T>::Node::Node(const T&, cop4530::List<T>::Node*, cop4530::List<T>::Node*) [with T = int]’
List.cpp: In constructor ‘cop4530::List<T>::iterator::iterator(cop4530::List<T>::Node*) [with T = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]’:
List.cpp:247: instantiated from ‘typename cop4530::List<T>::iterator cop4530::List<T>::begin() [with T = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]’
test_list.cpp:134: instantiated from here
List.cpp:112: error: no match for ‘operator=’ in ‘*((cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::iterator*)this)->cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::iterator::<anonymous>.cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::const_iterator::current = p’
List.h:11: note: candidates are: cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::Node& cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::Node::operator=(const cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::Node&)
make: *** [test_list.o] Error 1
Most importantly - Here is the exact code I am trying to fix:
template <class T>
typename List<T>::iterator List<T>::begin()
{ // iterator to first element
if (!empty())
return iterator( head->next );
}
Here is the full header:
#ifndef DL_LIST_H
#define DL_LIST_H
#include <iostream>
namespace cop4530 {
template <typename T>
class List {
private:
// nested Node class
struct Node {
T data;
Node *prev;
Node *next;
Node(const T & d = T(), Node *p = NULL, Node *n = NULL)
: data(d), prev(p), next(n) {}
};
public:
//nested const_iterator class
class const_iterator {
public:
const_iterator(); // default zero parameter constructor
const T & operator*() const; // operator*() to return element
// increment/decrement operators
const_iterator & operator++();
const_iterator operator++(int);
const_iterator & operator--();
const_iterator operator--(int);
// comparison operators
bool operator==(const const_iterator &rhs) const;
bool operator!=(const const_iterator &rhs) const;
protected:
Node *current; // pointer to node in List
T & retrieve() const; // retrieve the element refers to
const_iterator(Node *p); // protected constructor
friend class List<T>;
};
// nested iterator class
class iterator : public const_iterator {
public:
iterator() {}
T & operator*();
const T & operator*() const;
// increment/decrement operators
iterator & operator++();
iterator operator++(int);
iterator & operator--();
iterator operator--(int);
protected:
iterator(Node *p);
friend class List<T>;
};
public:
// constructor, desctructor, copy constructor
List(); // default zero parameter constructor
List(const List &rhs); // copy constructor
// num elements with value of val
explicit List(int num, const T& val = T());
// constructs with elements [start, end)
List(const_iterator start, const_iterator end);
~List(); // destructor
// assignment operator
const List& operator=(const List &rhs);
// member functions
int size() const; // number of elements
bool empty() const; // check if list is empty
void clear(); // delete all elements
void reverse(); // reverse the order of the elements
T &front(); // reference to the first element
const T& front() const;
T &back(); // reference to the last element
const T & back() const;
void push_front(const T & val); // insert to the beginning
void push_back(const T & val); // insert to the end
void pop_front(); // delete first element
void pop_back(); // delete last element
void remove(const T &val); // remove all elements with value = val
// print out all elements. ofc is deliminitor
void print(std::ostream& os, char ofc = ' ') const;
iterator begin(); // iterator to first element
const_iterator begin() const;
iterator end(); // end marker iterator
const_iterator end() const;
iterator insert(iterator itr, const T& val); // insert val ahead of itr
iterator erase(iterator itr); // erase one element
iterator erase(iterator start, iterator end); // erase [start, end)
private:
int theSize; // number of elements
Node *head; // head node
Node *tail; // tail node
void init(); // initialization
};
// overloading comparison operators
template <typename T>
bool operator==(const List<T> & lhs, const List<T> &rhs);
template <typename T>
bool operator!=(const List<T> & lhs, const List<T> &rhs);
// overloading output operator
template <typename T>
std::ostream & operator<<(std::ostream &os, const List<T> &l);
// include the implementation file here
#include "List.cpp"
} // end of namespace 4530
#endif
Here is most of the .cpp:
using namespace std;
// --------------------- CONST_ITERATOR --------------------- //
template <class T>
List<T>::const_iterator::const_iterator()
{ // default zero-parameter constructor. Set pointer current to NULL.
current = NULL;
}
template <class T>
const T& List<T>::const_iterator::operator*() const
{ // returns a reference to the corresponding element in the list by calling retrieve() member function.
return retrieve();
}
template <class T>
typename List<T>::const_iterator& List<T>::const_iterator::operator++()
{
current = current->next;
return *this;
}
template <class T>
typename List<T>::const_iterator List<T>::const_iterator::operator++(int)
{
const_iterator old = *this;
++( *this );
return old;
}
template <class T>
typename List<T>::const_iterator& List<T>::const_iterator::operator--()
{
current = current->prev;
return *this;
}
template <class T>
typename List<T>::const_iterator List<T>::const_iterator::operator--(int)
{
const_iterator old = *this;
--( *this );
return old;
}
template <class T>
bool List<T>::const_iterator::operator==(const const_iterator &rhs) const
{return current == rhs.current;}
template <class T>
bool List<T>::const_iterator::operator!=(const const_iterator &rhs) const
{return !( *this == rhs );}
template <class T>
T& List<T>::const_iterator::retrieve() const
{ // return a reference to the corresponding element in the list.
return current->data;
}
template <class T>
List<T>::const_iterator::const_iterator(Node *p)
{ // one-parameter constructor
// Set pointer current to the given node pointer p.
current = p;
}
// --------------------- ITERATOR --------------------- //
template <typename T>
T& List<T>::iterator::operator*()
{
return List<T>::iterator::retrieve();
}
template <typename T>
const T& List<T>::iterator::operator*() const
{
return List<T>::iterator::retrieve();
}
template <class T>
typename List<T>::iterator& List<T>::iterator::operator++()
{
*this->current = *this->current->next;
return *this;
}
template <class T>
typename List<T>::iterator List<T>::iterator::operator++(int)
{
iterator old = *this;
++( *this );
return old;
}
template <class T>
typename List<T>::iterator& List<T>::iterator::operator--()
{
*this->current = *this->current->prev;
return *this;
}
template <class T>
typename List<T>::iterator List<T>::iterator::operator--(int)
{
iterator old = *this;
--( *this );
return old;
}
template <class T>
List<T>::iterator::iterator(Node *p)
{ // one-parameter constructor
// Set current to the given node pointer p
*this->current = p;
}
// --------------------- LIST --------------------- //
template <class T>
List<T>::List()
{ init(); }
template <class T>
List<T>::List( const List & rhs )
{ // Copy constructor
init();
*this = rhs;
}
template <class T>
List<T>::List(int num, const T& val)
{ //Constructs a list with num elements, all initialized with value val
init();
iterator itr = begin();
for (int i = 0; i < num; ++i)
{
insert(itr, val);
++itr;
}
}
template <class T>
List<T>::~List()
{ // Destructor
clear();
delete head;
delete tail;
}
template <class T>
const typename List<T>::List& List<T>::operator=(const List &rhs)
{ // Assignment operator
iterator ritr = rhs.first();
iterator itr = begin();
if( this != &rhs )
{
clear();
for( ; ritr != NULL; ritr++, itr++ )
insert( ritr.retrieve( ), itr );
}
return *this;
}
template <class T>
int List<T>::size() const
{ // return the number of elements in the List
return theSize;
}
template <class T>
bool List<T>::empty() const
{ // check if list is empty
return head->next == NULL;
}
template <class T>
void List<T>::clear()
{ // delete all elements
while( !empty() )
erase( begin() );
}
template <class T>
T& List<T>::front()
{ // reference to the first element
return head->next->data;
}
template <class T>
typename List<T>::iterator List<T>::begin()
{ // iterator to first element
if (!empty())
return iterator( head->next );
}
template <class T>
typename List<T>::iterator List<T>::end()
{ // end marker iterator
if (!empty())
return iterator( tail->prev );
}
template <class T>
typename List<T>::iterator List<T>::insert( iterator itr, const T & x )
{
Node *p = itr.current;
theSize++;
return iterator( p->prev = p->prev->next = new Node( x, p->prev, p) );
}
template <class T>
typename List<T>::iterator List<T>::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;
}
template <class T>
typename List<T>::iterator List<T>::erase( iterator start, iterator end )
{
for( iterator itr = start; itr != end; )
itr = erase( itr );
return end;
}
template <class T>
void List<T>::init()
{ // Initaialize the member variables of a List
theSize = 0;
head = new Node;
tail = new Node;
head->next = tail;
tail->prev = head;
}
Let me first tell you (a) I know they are in separate files which is a big no-no for class templates, but I cannot change it for this assignment. (b) This is for homework. (c) I've tried a few variations of this code that fetches either more or less compile errors along the same vein. If you help me I will be so happy... Been at this for an hour or two.
Thanks! If you need any more info, let me know!
This is your problem:
return iterator itr( head->next );
You should probably be doing this:
return iterator( head->next );
The code as you have it is trying to declare an iterator named itr, which is a statement, not an expression (as required by return).
I'm not using the same compiler as you, but; I included the header in the cpp rather than the other way around, added a 'using' for your namespace in the cpp, and fixed the function signature of the implementation of operator= and that got what you've given us to build.
So, I'm building an implementation of List for a programming exercise. So far I have this:
#include <iostream>
#include <algorithm>
using namespace std;
template <class T> class Link;
template <class T> class List_iterator;
template <class T>
class List
{
public:
typedef List_iterator<T> iterator;
List();
List(const List<T> & l);
~List();
bool empty() const;
unsigned int size() const;
T & back() const;
T & front() const;
void push_front(const T & x);
void push_back(const T & x);
void pop_front();
void pop_back();
iterator begin() const;
iterator end() const;
void insert(iterator pos, const T & x);
void erase(iterator & pos);
List<T> & operator=(const List<T> & l);
protected:
Link<T> * first_link;
Link<T> * last_link;
unsigned int my_size;
};
template <class T>
List<T>::List()
{
first_link = 0;
last_link = 0;
my_size = 0;
}
template <class T>
List<T>::List(const List & l)
{
first_link = 0;
last_link = 0;
my_size = 0;
for (Link<T> * current = l.first_link; current != 0; current = current -> next_link)
push_back(current -> value);
}
template <class T>
typename List<T>::iterator List<T>::begin() const
{
return iterator(first_link);
}
template <class T>
class Link
{
private:
Link(const T & x): value(x), next_link(0), prev_link(0) {}//pg. 204
T value;
Link<T> * next_link;
Link<T> * prev_link;
friend class List<T>;
friend class List_iterator<T>;
};
template <class T> class List_iterator
{
public:
typedef List_iterator<T> iterator;
List_iterator(Link<T> * source_link): current_link(source_link) { }
List_iterator(): current_link(0) { }
List_iterator(List_iterator<T> * source_iterator): current_link(source_iterator.current_link) { }
T & operator*(); // dereferencing operator
iterator & operator=(const iterator & rhs);
bool operator==(const iterator & rhs) const;
bool operator!=(const iterator & rhs) const;
iterator & operator++();
iterator operator++(int);
iterator & operator--();
iterator operator--(int);
protected:
Link<T> * current_link;
friend class List<T>;
};
template <class T>
T & List_iterator<T>::operator*()
{
return current_link -> value;
}
template <class T>
List_iterator<T> & List_iterator<T>::operator++()
{
current_link = current_link -> next_link;
return *this;
}
template <class T>
void List<T>::push_back(const T & x)
{
link<T> * last_link = new link<T> (x);
if (empty())
first_link = last_link;
else
{
last_link->prev_link = last_link;
last_link->prev_link = last_link;
last_link = last_link;
}
}
template <class T>
typename List<T>::iterator List<T>::end() const
{
return iterator(last_link);
}
int main()
{
List<int> l;
l.push_back(44); // list = 44
l.push_back(33); // list = 44, 33
l.push_back(11); // list = 44, 33, 11
l.push_back(22); // list = 44, 33, 11, 22
List<int> m(l);
List<int>::iterator itr(m.begin());
while (itr != m.end()) {
cout << *itr << endl;
itr++;
}
}`
I'm having a lot of trouble with my push_back() function and I'm not quite sure what's wrong. Can anyone point out my errors?
Updated Code In Progress:
#include <iostream>
#include <algorithm>
using namespace std;
template <class T> class Link;
template <class T> class List_iterator;
template <class T>
class List
{
public:
typedef List_iterator<T> iterator;
List();
List(const List<T> & l);
~List();
bool empty() const;
unsigned int size() const;
T & back() const;
T & front() const;
void push_front(const T & x);
void push_back(const T & x);
void pop_front();
void pop_back();
iterator begin() const;
iterator end() const;
void insert(iterator pos, const T & x);
void erase(iterator & pos);
List<T> & operator=(const List<T> & l);
protected:
Link<T> * first_link;
Link<T> * last_link;
unsigned int my_size;
};
template <class T>
List<T>::List()
{
first_link = 0;
last_link = 0;
my_size = 0;
}
template <class T>
List<T>::List(const List & l)
{
first_link = 0;
last_link = 0;
my_size = 0;
for (Link<T> * current = l.first_link; current != 0; current = current -> next_link)
push_back(current -> value);
}
template <class T>
typename List<T>::iterator List<T>::begin() const
{
return iterator(first_link);
}
template <class T>
class Link
{
private:
Link(const T & x): value(x), next_link(0), prev_link(0) {}//pg. 204
T value;
Link<T> * next_link;
Link<T> * prev_link;
friend class List<T>;
friend class List_iterator<T>;
};
template <class T> class List_iterator//pg.207
{
public:
typedef List_iterator<T> iterator;
List_iterator(Link<T> * source_link): current_link(source_link) { }
List_iterator(): current_link(0) { }
List_iterator(List_iterator<T> * source_iterator): current_link(source_iterator.current_link) { }
T & operator*(); // dereferencing operator
iterator & operator=(const iterator & rhs);
bool operator==(const iterator & rhs) const;
bool operator!=(const iterator & rhs) const;
iterator & operator++();
iterator operator++(int);
iterator & operator--();
iterator operator--(int);
protected:
Link<T> * current_link;
friend class List<T>;
};
template <class T>
T & List_iterator<T>::operator*()
{
return current_link -> value;
}
template <class T>
List_iterator<T> & List_iterator<T>::operator++()
{
current_link = current_link -> next_link;
return *this;
}
template <class T>
void List<T>::push_back(const T & x)
{
Link<T> * new_link = new Link<T> (x);
if (first_link = 0)
first_link = last_link = new_link;
else
{
new_link->prev_link = last_link;
last_link->next_link = new_link;
last_link = new_link;
}
my_size++;
}
template <class T>
typename List<T>::iterator List<T>::end() const
{
return iterator(last_link);
}
template <class T>
List <T>::~List()
{
Link <T> * first = first_link;
while (first != 0)
{
Link <T> * next = first->next_link;
delete first;
first = next;
}
}
template<class T>
bool List_iterator<T>::operator==(const iterator & rhs) const
{
return ( this->current_link == rhs.current_link );
}
template <class T>
bool List_iterator<T>::operator!=(const iterator & rhs) const
{
return !( *this == rhs );
}
int main()
{
List<int> l;
l.push_back(44); // list = 44
l.push_back(33); // list = 44, 33
l.push_back(11); // list = 44, 33, 11
l.push_back(22); // list = 44, 33, 11, 22
List<int> m(l);
List<int>::iterator itr(m.begin());
while (itr != m.end()) {
cout << *itr << endl;
++itr;
}
}
Mario's and Joe's answers are both valid (I would vote them up if I could).
Issue 1
Assuming that what you posted is your ACTUAL code for push_back() then Link<T> has the wrong case.
link<T> * last_link = new link<T> (x);
...should be:
Link<T> * last_link = new Link<T> (x);
Doing this gets rid of error: expected primary-expression before ‘>’ token (when compiled with g++)
Issue 2
An alternative to what Mario and Joe suggested, is to NOT hide your List<T>'s last_link. Use a temporary variable that does NOT have the same name as your class's data member. Change:
Link<T> * last_link = new Link<T> (x);
...to...
Link<T> * new_link = new Link<T> (x);
This will make your code clearer and you will be referencing the right node.
Issue 3
Following the above steps should make two logic errors in push_back() evident. I'm willing to assist, but I'm sure you will see it.
The following line creates a local variable named last_link inside push_back().
link<T> * last_link = new link<T> (x);
Referencing last_link after this will point to this local variable instead of the class member. You'll have to add this-> in front of the class ones, e.g. this->last_link = last_link;.
You have a local variable that is the same as a member variable name. When you're using last_link you're using the one that you just created in the push_back function. You're also assigning last_link->prev_link twice. Then also assigning last_link = last_link. I'd either change the name of the local variable, or add this-> in front of it.