1

I'm trying to test SinglyLinked List example in "Data Structure and Algorithm in c++" by Micheal T. Goodrich and etc. I added some details omitted by the author in order to make it runnable.

Here's the code:

#ifndef S_LINKED_LIST
#define S_LINKED_LIST

template <typename E>
class SLinkedList;

template <typename E>
class SNode
{
    private:
        E elem;
        SNode<E> * next;
        friend class SLinkedList<E>;
};
template <typename E>
class SLinkedList
{
    private:
        SNode<E> * head;
    public:
        SLinkedList();
        ~SLinkedList();
        bool empty() const;
        const E& front() const;
        void addFront(const E& e);
        void removeFront();
};
#endif /*SLinkedList.h*/

Implementation:

#include "SLinkedList.h"
#include <iostream>

template <typename E>
SLinkedList<E>::SLinkedList():head(NULL){}

template <typename E>
SLinkedList<E>::~SLinkedList()
{while(!empty()) removeFront();}

template <typename E>
bool SLinkedList<E>::empty() const
{return head == NULL;}

template <typename E>
const E& SLinkedList<E>::front() const
{return head->elem;}

template <typename E>
void SLinkedList<E>::addFront(const E& e)
{   
    SNode<E> * newNode = new SNode<E>;
    newNode->elem = e;
    newNode->next = head;
    head = newNode;
}

template <typename E>
void SLinkedList<E>::removeFront()
{
    SNode<E> * old = head;
    head = old->next;
    delete old;
}/*SLinkedList.cpp*/

test file:

#include <iostream>
#include "SLinkedList.h"

int main()
{
    SLinkedList<std::string> newlist;
    newlist.addFront("MSP");
    std::cout << newlist.front();
    return 0;
}/*test_slinkedlist.cpp*/

After running g++ -c SLinkedList.cpp and g++ -c test_slinkedlist.cpp I got object files SLinkedList.o and test_slinkedlist.o without error. but when I run g++ -o result test_slinkedlist.o SLinkedList.o, I got linker errors:

Undefined symbols for architecture x86_64:
...
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I spend a day debugging this linker issue but can't find. I suspect it's obvious.

Operating System: OS X


full error message:

Undefined symbols for architecture x86_64:
  "SLinkedList<std::__1::basic_string<char,     std::__1::char_traits<char>, std::__1::allocator<char> >     >::addFront(std::__1::basic_string<char, std::__1::char_traits<char>,     std::__1::allocator<char> > const&)", referenced from:
      _main in test_slinkedlist.o
  "SLinkedList<std::__1::basic_string<char,     std::__1::char_traits<char>, std::__1::allocator<char> > >::SLinkedList()",     referenced from:
      _main in test_slinkedlist.o
  "SLinkedList<std::__1::basic_string<char,     std::__1::char_traits<char>, std::__1::allocator<char> >     >::~SLinkedList()", referenced from:
      _main in test_slinkedlist.o
  "SLinkedList<std::__1::basic_string<char,     std::__1::char_traits<char>, std::__1::allocator<char> > >::front() const",     referenced from:
      _main in test_slinkedlist.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Jon
  • 118
  • 1
  • 8
  • 1
    Possible duplicate of [Why can templates only be implemented in the header file?](http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – Revolver_Ocelot Jul 25 '16 at 07:24
  • @Revolver_Ocelot I can see the reason you said it's duplicate, but I think it might be helpful for people like me who switch from C to C++ and inherit habits from C and don't know what key word to search to have this question and your comment to know where to go next. Thanks for your advise. – Jon Jul 25 '16 at 14:42

2 Answers2

0

Implementation:

You didn't say it, but we can assume that implementation is in SLinkedList.cpp.

Undefined symbols for architecture x86_64:
...

In the ..., you've omitted the most important part of the error message. However, we can guess that SLinkedList<std::string>::addFront(...) is among the unresolved symbols.

Your problem is that the body of the template method (i.e. the implementation) must be present in every compilation unit in which it is used, and you've violated that rule by putting this body into a separate .cpp file, which is not visible to the compiler when it compiles test_slinkedlist.cpp.

This is why template bodies are most often included in the SLinkedList.h (note the .h, not the .cpp). See also this answer.

P.S. Technically, the "must" above is incorrect: you could also use explicit instantiation to make the compiler produce template method bodies that you need. But that's a bit more advanced solution than putting the implementation into .h file.

Community
  • 1
  • 1
Employed Russian
  • 199,314
  • 34
  • 295
  • 362
0

In C++, you can't compile and link uninstantiated templates as a separate compilation unit.

SLinkedList.cpp has no instantiated templates, so it will compile into essentially nothing.

This is why template-only libraries are always header-only.

You have a couple of choices.

Either instantiate your SLinkedList within SLinkedList.cpp's compilation unit to let the compiler generate actual code for the template instance, or combine your header and cpp into one file.

nurettin
  • 11,090
  • 5
  • 65
  • 85