0

I'm working on an assignment to make a linked list of Card objects, and I'm running into a linker error when compiling. In the Deck class cpp, I get an udefined reference to the overloaded << operator and to the LinkedList constructor for Card. I've tried compiling in both Dev C++ and g++

In dev I get the following error:

C:\Users\Chris\Dropbox\mohroo-2a\Deck.o Deck.cpp:(.text+0x1c): undefined reference to `operator<<(std::ostream&, LinkedList<Card> const&)'
C:\Users\Chris\Dropbox\mohroo-2a\Deck.o Deck.cpp:(.text+0x3e): undefined reference to `LinkedList<Card>::LinkedList()'
c:\program files (x86)\dev-cpp\mingw64\x86_64-w64-mingw32\bin\ld.exe            C:/Users/Chris/Dropbox/mohroo-2a/Deck.o: bad reloc address 0x0 in section `.pdata'
c:\program files (x86)\dev-cpp\mingw64\x86_64-w64-mingw32\bin\ld.exe    final link failed: Invalid operation
F:\Documents\collect2.exe   [Error] ld returned 1 exit status

in g++:

In file included from Deck.h:4:0,
             from Deck.cpp:1:
LinkedList.h:9:76: warning: friend declaration ‘std::ostream& operator<<(std::ostream&, const LinkedList<T>&)’ declares a non-template function [-Wnon-template-friend]
  friend std::ostream &operator<<(std::ostream &ostr, const LinkedList<T> &l);
                                                                        ^
LinkedList.h:9:76: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)
In file included from LinkedList.cpp:1:0:
LinkedList.h:9:76: warning: friend declaration ‘std::ostream& operator<<(std::ostream&, const LinkedList<T>&)’ declares a non-template function [-Wnon-template-friend]
  friend std::ostream &operator<<(std::ostream &ostr, const LinkedList<T> &l);
                                                                        ^
LinkedList.h:9:76: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)
In file included from Deck.h:4:0,
             from program.cpp:2:
LinkedList.h:9:76: warning: friend declaration ‘std::ostream& operator<<(std::ostream&, const LinkedList<T>&)’ declares a non-template function [-Wnon-template-friend]
  friend std::ostream &operator<<(std::ostream &ostr, const LinkedList<T> &l);
                                                                        ^
LinkedList.h:9:76: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)
/tmp/cc3tT0cD.o:Deck.cpp:(.text+0x1c): undefined reference to `operator<<(std::ostream&, LinkedList<Card> const&)'
/tmp/cc3tT0cD.o:Deck.cpp:(.text+0x1c): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `operator<<(std::ostream&, LinkedList<Card> const&)'
/tmp/cc3tT0cD.o:Deck.cpp:(.text+0x3e): undefined reference to `LinkedList<Card>::LinkedList()'
/tmp/cc3tT0cD.o:Deck.cpp:(.text+0x3e): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `LinkedList<Card>::LinkedList()'
/usr/lib/gcc/x86_64-pc-cygwin/4.8.1/../../../../x86_64-pc-cygwin/bin/ld:     /tmp/cc3tT0cD.o: bad reloc address 0x0 in section `.pdata'
collect2: error: ld returned 1 exit status

Here is the code:

Deck.h

#ifndef DECK_H
#define DECK_H
#include <ostream>
#include "LinkedList.h"
#include "Card.h"

class Deck
{
    friend std::ostream &operator<<(std::ostream &ostr, const Deck &d);

    public:
        Deck();

    private:
        LinkedList<Card> theDeck;
        Node<Card> *top;
};

#endif

Deck.cpp

#include "Deck.h"

std::ostream &operator<<(std::ostream &ostr, const Deck &d)
{
    ostr << d.theDeck;
    return ostr;
}

Deck::Deck()
{

}

LinkedList.h

#ifndef LINKEDLIST_H
#define LINKEDLIST_H
#include <ostream>
#include "Node.h"

template <typename T>
class LinkedList
{

    template <typename K>
    friend std::ostream &operator<<(std::ostream &ostr, const LinkedList<K> &l);

    public:
        LinkedList();
        ~LinkedList();
        void addNode(Node<T> *newNode);
        Node<T> *head;
};

template <typename T>
LinkedList<T>::LinkedList()
{
    head = 0;
}

template <typename T>
LinkedList<T>::~LinkedList()
{
    Node<T> *ptr = head;
    while(ptr != 0)
    {
        Node<T> *temp = ptr->next;
        delete ptr;
        ptr = temp;
    }
}

template <typename T>
void LinkedList<T>::addNode( Node<T> *newNode)
{
    Node<T> *ptr = head;
    while(ptr != 0)
    {
        ptr = ptr->next;
    }
    ptr->next = newNode;
}

template <typename K>
friend std::ostream &operator<<(std::ostream &ostr, const LinkedList<K> &l)
{
    ostr << "[ ";
    Node<T> *ptr = l.head;
    while(ptr != 0)
    {
        ostr << ptr->data << " ";
        ptr = ptr->next;
    }
    ostr << "]";
    return ostr;
}

#endif

Node.h

#ifndef NODE_H
#define NODE_H

template <typename T>
class Node
{

    public:
        Node();
        Node(const T &nodeData);
        Node(const Node<T> *nextNode);
        Node(const T &nodeData, const Node *nextNode);

        T data;
        Node *next;

};

template <typename T>
Node<T>::Node()
    :data()
{
    next = 0;
}

template <typename T>
Node<T>::Node(const T &nodeData)
{
    data = nodeData;
    next = 0;
}

template <typename T>
Node<T>::Node(const Node<T> *nextNode)
    :data()
{
    next = nextNode;
}

template <typename T>
Node<T>::Node(const T &nodeData, const Node *nextNode)
{
    data = nodeData;
    next = nextNode;
}

#endif

Card.h

#ifndef CARD_H
#define CARD_H
#include <ostream>
class Card
{

      friend std::ostream &operator << (std::ostream &ostr, const Card &c);

      private:
            int value; //Ranges from 2-14, where Ace = 14, Jack = 11, Queen = 12, King = 13
            int suit; //Ranges from 1-4, where 1 = Clubs, 2 = Diamonds, 3 = Hearts, 4 = Spades

      public:
             Card();
             Card(int, int);
             Card operator=(const Card &crd);
             Card (const Card &obj);
             void setValue(int);
             void setSuit(int);
             int getValue();
             int getSuit();

};


#endif

Card.cpp

#include "Card.h"

std::ostream &operator<<(std::ostream &ostr, const Card &c)
{
    switch(c.value)
    {
    case 2:
        ostr << "Two";
        break;
    case 3:
        ostr << "Three";
        break;
    case 4:
        ostr << "Four";
        break;
    case 5:
        ostr << "Five";
        break;
    case 6:
        ostr << "Six";
        break;
    case 7:
        ostr << "Seven";
        break;
    case 8:
        ostr << "Eight";
        break;
    case 9:
        ostr << "Nine";
        break;
    case 10:
        ostr << "Ten";
        break;
    case 11:
        ostr << "Jack";
        break;
    case 12:
        ostr << "Queen";
        break;
    case 13:
        ostr << "King";
        break;
    case 14:
        ostr << "Ace";
        break;
    }
    ostr << " of ";

    switch(c.suit)
    {
    case 1:
        ostr << "Clubs";
        break;
    case 2:
        ostr << "Diamonds";
        break;
    case 3:
        ostr << "Hearts";
        break;
    case 4:
        ostr << "Spades";
        break;
    }
    return ostr;
}

Card::Card()
{
            value = 0;
            suit = 0;
};


Card::Card(const Card &obj)
{
                 value = obj.value;
                 suit = obj.suit;
};


Card Card::operator=(const Card &crd)
{
     suit = crd.suit;
     value = crd.value;

     return *this;
};


Card::Card(int tvalue, int tsuit)
{
               value = tvalue;
               suit = tsuit;
};


void Card::setValue(int tempValue)
{
     value = tempValue;
}


void Card::setSuit(int tempSuit)
{
     suit = tempSuit;
}


int Card::getValue()
{
    return value;
}


int Card::getSuit()
{
    return suit;
}

and lastly program.cpp

#include <iostream>
#include "Deck.h"
using namespace std;

int main()
{
    Card someCard(2,4);
    Card otherCard(14,1);
    cout << someCard << "\n" << otherCard << endl;


}

So any ideas? I've tried looking this up and it seems like a linker error but when I compile using g++ *.cpp -o main it produces the above error. I've hit a wall with what to do next. Thanks in advance

EDIT:

LinkedList.h now looks like this

#ifndef LINKEDLIST_H
#define LINKEDLIST_H
#include <ostream>
#include "Node.h"

template <typename T>
class LinkedList
{
    template <typename K>
    friend std::ostream &operator<<(std::ostream &ostr, const LinkedList<K> &l);


    public:
        LinkedList();
        ~LinkedList();
        void addNode(Node<T> *newNode);
        Node<T> *head;
};

template <typename T>
LinkedList<T>::LinkedList()
{
    head = 0;
}

template <typename T>
LinkedList<T>::~LinkedList()
{
    Node<T> *ptr = head;
    while(ptr != 0)
    {
        Node<T> *temp = ptr->next;
        delete ptr;
        ptr = temp;
    }
}

template <typename T>
void LinkedList<T>::addNode( Node<T> *newNode)
{
    Node<T> *ptr = head;
    while(ptr != 0)
    {
        ptr = ptr->next;
    }
    ptr->next = newNode;
}

template <typename K>
friend std::ostream &operator<<(std::ostream &ostr, const LinkedList<K> &l)
{
    ostr << "[ ";
    Node<T> *ptr = l.head;
    while(ptr != 0)
    {
        ostr << ptr->data << " ";
        ptr = ptr->next;
    }
    ostr << "]";
    return ostr;
}

#endif
cpmpal
  • 3
  • 1
  • 3
  • How much of this did you write since the last successful test? Start small and simple, add complexity a little at a time, test at every step, and *never add to code that doesn't work.* – Beta Feb 02 '14 at 22:28
  • Honestly I tested in steps. I wrote Card module checked card, wrote Node checked Node, wrote LinkedList checked LinkedList, then Deck raised issues. As I write this though, I'm not sure I properly tested LinkedList before going to to Deck. Thanks for the advice though – cpmpal Feb 02 '14 at 22:34

2 Answers2

2

Aside the fact that as pointed out above, templates have to be in header files, the actual problem is that the friend function is actually not within the scope of the class, so you cannot use the typename T, it can shadow the class typename. Use a different one, try K:

template <typename T>
class LinkedList
{
    template <typename K>
    friend std::ostream &operator<<(std::ostream &ostr, const LinkedList<K> &l);
...

You can still use T for the implementation of the operator. If using K:

template <typename K>
std::ostream &operator<<(std::ostream &ostr, const LinkedList<K> &l)
{
    ostr << "[ ";
    Node<K> *ptr = l.head;
drodri
  • 5,157
  • 15
  • 21
  • I changed my code to put the implementations in just the header original header files, so I believe it's doing the same thing – cpmpal Feb 02 '14 at 22:06
  • I updated my post and tried this as you can see at the end of my post. I still get an undefined reference to operator<<. – cpmpal Feb 02 '14 at 22:36
  • Could you please check the last version of source files? I have been able to build and link the program after moving myself node.cpp and linkedlist.cpp to headers. If you post exactly your files I will get them again and try to build them again. – drodri Feb 02 '14 at 22:53
  • You have a couple of mistakes in the implementation of the LinkedList.h friend << operator, remove the friend keyword and ensure that K is used inside the function for Node, not T; that builds and works for me – drodri Feb 02 '14 at 23:38
0

You should write all of your template classes inside header files. Compiler doesn't know which classes you will use with as a template while compiling a .cpp file and so it doesn't generate anything at all.

Alex Telishev
  • 2,264
  • 13
  • 15
  • I included the content of the Node and LinkedList cpp's after the declartion of each class in the header file, but I'm still getting an undefined reference error in Deck.cpp for the ostream operator overload – cpmpal Feb 02 '14 at 21:56
  • You should write `template friend std::ostream &operator<<(std::ostream &ostr, const LinkedList &l);` – Alex Telishev Feb 02 '14 at 22:04
  • I put the implementation in the declaration of `std::ostream &operator<<(std::ostream &ostr, const LinkedList &l)` but I still get the same undefined reference. I tried adding another template declaration before this but it was still the same error. I edited my post to show the new code. Thanks – cpmpal Feb 02 '14 at 22:16
  • No, operator<< should be outside of the class. Write `template class LinkedList { public: LinkedList(); ~LinkedList(); void addNode(Node *newNode); Node *head; }; template std::ostream &operator<<(std::ostream &ostr, const LinkedList &l) { /// some code }` – Alex Telishev Feb 02 '14 at 22:21
  • don't i need to make operator<< a friend of LinkedList? – cpmpal Feb 02 '14 at 22:27
  • As every member in LinkedList is public you don't need friends. But if you really want to make it friend, add `template friend std::ostream &operator<<(std::ostream &ostr, const LinkedList &l);` line inside the LinkedList class. – Alex Telishev Feb 02 '14 at 22:30