-1

I am attempting to compile a new class template within the namespace cop4530. Do you have any idea why I am getting this back? These functions do exist and the compiler isn't helping me by saying much about the specifics. I have done what this thread says already, but I still get undefined symbols

g++ -o proj2.x  test_list.o
Undefined symbols for architecture x86_64:
  "cop4530::List<int>::iterator::operator++(int)", referenced from:
      _main in test_list.o
  "cop4530::List<int>::iterator::operator*()", referenced from:
      _main in test_list.o
  "cop4530::List<int>::iterator::operator++()", referenced from:
      _main in test_list.o
      cop4530::List<int>::List(int, int const&)in test_list.o
  "cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::iterator::operator++(int)", referenced from:
      _main in test_list.o
  "cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::iterator::operator*()", referenced from:
      _main in test_list.o
  "cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::iterator::operator++()", referenced from:
      _main in test_list.o
      cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::List(int, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)in test_list.o
  "cop4530::List<int>::iterator::iterator(cop4530::List<int>::Node*)", referenced from:
      cop4530::List<int>::erase(cop4530::List<int>::iterator)in test_list.o
      cop4530::List<int>::clear()  in test_list.o
      cop4530::List<int>::insert(cop4530::List<int>::iterator, int const&)in test_list.o
  "cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::iterator::iterator(cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::Node*)", referenced from:
      cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::erase(cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::iterator)in test_list.o
      cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::clear()in test_list.o
      cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::insert(cop4530::List<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::iterator, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)in test_list.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
make: *** [List_executable] Error 1

My header was written by textbook and nothing has been changed(or can be changed for this project), so the problem isn't there. Same with the driver 'test_list'. At the end of the header file there is an include (what the question [its not called a thread, I'm told] above said to do.)

Header anyway:

#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 the .cpp file. Its not completely finished:

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 <class T>
T& List<T>::iterator::operator*()
{
  return retreive();
}

template <class T>
const T& List<T>::iterator::operator*() const
{
  return retreive();
}

template <class T>
List<T>::iterator & List<T>::iterator::operator++()
{
  current = current->next;
  return *this;
}

template <class T>
List<T>::iterator List<T>::iterator::operator++(int)
{
  const_iterator old = *this;
  ++( *this );
  return old;
}

template <class T>
List<T>::iterator & List<T>::iterator::operator--()
{
  current = current->prev;
  return *this;
}

template <class T>
List<T>::iterator List<T>::iterator::operator--(int)
{
  const_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
  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(const_iterator start, const_iterator end)
{ // Constructs a List w/ elements from another List between
  // start and end. [includes start, not end) 
  ///////////
}

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
  List<T>::iterator ritr = rhs.first();
  List<T>::iterator itr = begin();

  if( this != &rhs )
    {
      clear();
      for( ; !ritr.isPastEnd( ); ritr.advance( ), itr.advance( ) )
    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( !isEmpty( ) )
    erase( begin().retreive() );
}

template <class T>
void List<T>::reverse()
{ // reverse the order of the elements
  /////////////
}

template <class T>
T& List<T>::front()
{ // reference to the first element
  return head->next->data;
}

template <class T>
T& List<T>::back()
{ // reference to the last element
  return tail->prev->data;
}

template <class T>
void List<T>::push_front(const T & val)
{ // insert to the beginning

}

template <class T>
void List<T>::push_back(const T & val)
{ // insert to the end

}

template <class T>
void List<T>::pop_front()
{ // delete first element

}

template <class T>
void List<T>::pop_back()
{ // delete last element

}

template <class T>
void List<T>::remove(const T &val)
{ // remove all elements with value = val

}

template <class T>
void List<T>::print(std::ostream& os, char ofc) const
{ // print out all elements. ofc is deliminitor

}

template <class T>
typename List<T>::iterator List<T>::begin()
{ // iterator to first element
  return List<T>::iterator( head->next );
}

template <class T>
typename List<T>::iterator List<T>::end()
{ // end marker iterator
  return List<T>::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;
}


// --------- OVERLOADS --------- //

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)
{

}

template <typename T>
std::ostream & operator<<(std::ostream &os, const List<T> &l)
{

}

Let me just add that I appreciate any and all help given. I am a student and I am trying to learn.

Community
  • 1
  • 1
jordaninternets
  • 233
  • 2
  • 6
  • 15
  • 1
    please consider including the header and definition of cop4530::List inside your question – Gigi Feb 07 '12 at 22:59
  • anyway, the methods of template classes are instanced at first use, so the definition for List and List must be visible when you use it – Gigi Feb 07 '12 at 23:05
  • My crystal ball says that you are missing the definition of those methods. However it refuses to answer any questions about why they are missing with **not enough information**. How should we help you, if you don't show us your code? – Grizzly Feb 07 '12 at 23:06
  • I posted some of it. Sorry! Wasn't sure if this was a more common problem that people would see and know what was wrong. – jordaninternets Feb 07 '12 at 23:06
  • Well, no, you haven't done what that other question stated (there are no "threads" on Stack Overflow, which is _not a forum_). Take those template definitions out of the .cpp. – Lightness Races in Orbit Feb 07 '12 at 23:06
  • Oh, but I have. **"so you either have to include function definitions in the header, or #include the cpp file prior to using the class (or do explicit instantiations if you have a limited number of them)"** Sorry for calling it a thread... Didn't realize overflowers were that particular. – jordaninternets Feb 07 '12 at 23:15
  • @jordaninternets: So maybe showing us the code where you use your type (and the `#includes`) as well as telling us any abitrary restrictions imposed by the exercise would be a good idea? Afterall I doubt we have many mind readers here. – Grizzly Feb 07 '12 at 23:19
  • I added all the code. I am sorry I didn't include it originally. I'm a student and have been at this for a few hours so my head is a little off right now. No need to downvote the question. Thank you for all the help. – jordaninternets Feb 07 '12 at 23:33

2 Answers2

2

Template class methods are instanced the first time they are used, so a definition for List<int> and List<string> must be visible from the calling unit.

Your textbook seems to assume compiler support for external template definitions, that GCC don't support.

An easy fix is to copy-paste the List.cpp file content inside the compilation unit you are trying to fix.

Gigi
  • 4,953
  • 24
  • 25
1

Now that you have posted your code, the answer is simple (and actually the same as the one for the question you linked):

The compiles has to see the definitions of template functions (and methods of template classes) at the point they are instantiated. Therefore template functions/methods should not be defined in a .cpp file. Put them in a header instead (and include that one at the point you are using the template).

Community
  • 1
  • 1
Grizzly
  • 19,595
  • 4
  • 60
  • 78
  • I cannot define these within the header file. That is part of the homework. I am not allowed to touch the header file. – jordaninternets Feb 07 '12 at 23:11
  • And you don't think mentioning that in your question would be a good idea? Anyway they need to be visible at the point they are used, so you need to put them in a header (though not necessarily the same they are declared in) and include that one, or define them directly in the compile unit where they are used (if there is only one). So if your homework forbids those you are out of options (if there are other options, they really shouldn't be expected in a homework (because they shouldn't be **tought**)). Technically you could `#include` a `.cpp` file, basically treating it as a header,but why? – Grizzly Feb 07 '12 at 23:16
  • I have an include `#include "List.cpp"` at the end of the header file. So I think it is considered to be within that file? Right? – jordaninternets Feb 07 '12 at 23:20
  • @jordaninternets: If you did that in the right place your code should work (although that is really not something you should do, files for inclusion should be headers (or some sort of `*.inl`, but definatly not `*.cpp`). Since it doesn't you obviously did something wrong. However in light of that information we can't tell you what without more information – Grizzly Feb 07 '12 at 23:23
  • All the code is up. Thank you for helping, I am sorry I didn't post it all immediately. I'm frustrated too. – jordaninternets Feb 07 '12 at 23:31