Problem with templates

I am trying to write a template class for a simple double linked list. Posted below are the definitions. In the .cpp file I have the implementation. I know for certain that I am including the .h and .cpp files in the project, but I get an error when I try to build:

error LNK2019: unresolved external symbol "public: __thiscall wcLinkedList<int>::~wcLinkedList<int>(void)" (?1?$wcLinkedList@H@@QAE@XZ) referenced in function _main

From mymain()here is the code that is causing the problem:

void main(int argc,char **argv) {

wcLinkedList<int> test;

getchar();

}


Where am I going wrong with these templates?
Thanks,
Graham


/*********************************************************************************************/

template<class T>

class wcLLNode {

private:

T value;

wcLLNode *next, *prev;

public:

wcLLNode();

wcLLNode(T value, wcLLNode *next, wcLLNode *prev);

~wcLLNode() {}

};

/*********************************************************************************************/

template<class T>

class wcLinkedList {

private:

wcLLNode<T> *head;//Points to the head of the list

wcLLNode<T> *tail;//Points to the end of the list

wcLLNode<T> *cur;//Index into the list

int count;//Number of nodes in the list

public:

wcLinkedList();//Default constructor

~wcLinkedList();//Default destructor

void Add(T value);//Add a node to the list

int Remove(void);

int Remove(T value);//Remove a node from the list

T Next(void);//Move to the next node in the list and return its ptr

T Previous(void);//Move to the previous node in the list and return its ptr

T First(void);//Move to the first node and return its ptr

T Last(void);//Move to the last node and return its ptr

inlineint Count(void) {returnthis->count; }//Return the number of nodes in the list

inlinebool AtHead(void) {returnthis->cur ==this->head; }//Check to see if we are at last node

inlinebool AtTail(void) {returnthis->cur ==this->tail; }//Check to see if iterator is at last node

};

[5988 byte] By [GrahamH] at [2007-12-16]
# 1
Hi,

this a problem with many C++ compilers. I think this is an implementation issue.
In many cases you must define the code in the header file.
Just write all the code in the header and do not use a *.cpp file for that.

Another solution is to in include an *.inl file in the header like:

#ifndef FOO_H
#define FOO_H

class foo
{
};

#include "foo.inl"

#endif

Just write all the *.cpp code into the *.inl file. This keeps code cleaner. I think you see. :-) There are maybe other solutions.If you do as i said it will work for you.

Bye
Martin

maddinthegreat at 2007-9-9 > top of Msdn Tech,Visual C++,Visual C++ Language...
# 2

I believe the errors could be due to not actually definning the default constructor and destructor. The sample is doing something like:

wcLinkedList(); //Default constructor
~wcLinkedList(); //Default destructor

If you are not going to define their implementation then you can just comment them out.

Thanks,
Ayman Shoukry
VC++ Team.

AymanShoukry at 2007-9-9 > top of Msdn Tech,Visual C++,Visual C++ Language...
# 3
Hi Ayman,
you're right. I didn't see.

Bye
Martin

maddinthegreat at 2007-9-9 > top of Msdn Tech,Visual C++,Visual C++ Language...
# 4

Thanks for the quick response you two. I am copying in the body of my .cpp so you can take a look. I do fully define the constructor and destructor for the classes.

I will try not putting code into a .cpp and see how that goes.

Thanks again.
Graham

/*****************************************************************************************/

template<class T>

wcLLNode<T>::wcLLNode() {

this->value = null;

this->next = null;

this->prev = null;

}

template<class T>

wcLLNode<T>::wcLLNode(T value, wcLLNode *next, wcLLNode *prev) {

this->value = value;

this->next = next;

this->prev = prev;

}

/*****************************************************************************************/

//Default constructor

template<class T>

wcLinkedList<T>::wcLinkedList() {

this->count = 0;

this->head = null;

this->tail = null;

this->cur = null;

}

//Default destructor

template<class T>

wcLinkedList<T>::~wcLinkedList() {

wcLLNode *tmpPtr = this->head;

while (tmpPtr != null) {

this->cur = tmpPtr->next;

delete tmpPtr;

tmpPtr = this->cur;

this->count--;

}

}

//Add a node to the list

template<class T>

void wcLinkedList<T>::Add(T value) {

wcLLNode *tmpNode;

if (this->count == 0) {

tmpNode = new wcLLNode(value, null, null);

this->head = tmpNode;

}

else {

= new wcLLNode(value, null, this->tail);

this->tail->next = tmpNode;

}

this->cur = tmpNode;

this->tail = tmpNode;

}

//Remove last node from the list

template <class T>

int wcLinkedList<T>::Remove(void) {

switch (this->count) {

case 0: return -1;

break;

case 1:

this->cur = null;

this->tail = null;

delete this->head;

break;

default:

this->cur = this->tail->prev;

delete this->tail;

this->tail = this->cur;

break;

}

this->count--;

return this->count;

}

//Remove a specific node from the list

template <class T>

int wcLinkedList<T>::Remove(T value) {

return -1;

}

//Move to the next node in the list and return its ptr

template <class T>

T wcLinkedList<T>::Next(void) {

if (this->cur == this->tail) return null;

this->cur = this->cur->next;

return this->cur->value;

}

//Move to the previous node in the list and return its ptr

template <class T>

T wcLinkedList<T>::Previous(void) {

if )this->cur == this->head) return null;

this->cur = this->cur->prev;

return this->cur->value;

}

//Move to the first node and return its ptr

template <class T>

T wcLinkedList<T>::First(void) {

this->cur = this->head;

return this->cur->value;

}

//Move to the last node and return its ptr

template <class T>

T wcLinkedList<T>::Last(void) {

this->cur = this->tail;

return this->cur->value;

}

GrahamH at 2007-9-9 > top of Msdn Tech,Visual C++,Visual C++ Language...
# 5

I did something like the following and it worked with no linker errors:

My compilation command is: cl /EHsc main.cpp.

I am using VC2005. Also, I have modified the destructor code to avoid some compile time errors.

main.cpp included temp.h where the template is declared.

//main.cpp
#include "temp.h"
#include <stdio.h>
#include <iostream>
template<class T>

wcLLNode<T>::wcLLNode() {

this->value = NULL;

this->next = NULL;

this->prev = NULL;

}

template<class T>

wcLLNode<T>::wcLLNode(T value, wcLLNode *next, wcLLNode *prev) {

this->value = value;

this->next = next;

this->prev = prev;

}

/*****************************************************************************************/

//Default constructor

template<class T>

wcLinkedList<T>::wcLinkedList() {

this->count = 0;

this->head = NULL;

this->tail = NULL;

this->cur = NULL;

}

//Default destructor

template<class T>

wcLinkedList<T>::~wcLinkedList() {

/*wcLLNode *tmpPtr = this->head;

while (tmpPtr != NULL) {

this->cur = tmpPtr->next;

delete tmpPtr;

tmpPtr = this->cur;

this->count--;

}*/

}

//Add a node to the list

template<class T>

void wcLinkedList<T>::Add(T value) {

wcLLNode *tmpNode;

if (this->count == 0) {

tmpNode = new wcLLNode(value, NULL, NULL);

this->head = tmpNode;

}

else {

= new wcLLNode(value, NULL, this->tail);

this->tail->next = tmpNode;

}

this->cur = tmpNode;

this->tail = tmpNode;

}

//Remove last node from the list

template <class T>

int wcLinkedList<T>::Remove(void) {

switch (this->count) {

case 0: return -1;

break;

case 1:

this->cur = NULL;

this->tail = NULL;

delete this->head;

break;

default:

this->cur = this->tail->prev;

delete this->tail;

this->tail = this->cur;

break;

}

this->count--;

return this->count;

}

//Remove a specific node from the list

template <class T>

int wcLinkedList<T>::Remove(T value) {

return -1;

}

//Move to the next node in the list and return its ptr

template <class T>

T wcLinkedList<T>::Next(void) {

if (this->cur == this->tail) return NULL;

this->cur = this->cur->next;

return this->cur->value;

}


//Move to the previous node in the list and return its ptr

template <class T>

T wcLinkedList<T>::Previous(void) {

if )this->cur == this->head) return NULL;

this->cur = this->cur->prev;

return this->cur->value;

}

//Move to the first node and return its ptr

template <class T>

T wcLinkedList<T>::First(void) {

this->cur = this->head;

return this->cur->value;

}

//Move to the last node and return its ptr

template <class T>

T wcLinkedList<T>::Last(void) {

this->cur = this->tail;

return this->cur->value;

}

void main(int argc, char **argv) {

wcLinkedList<int> test;

getchar();

}


//temp.h
/*********************************************************************************************/

template<class T>

class wcLLNode {

private:

T value;

wcLLNode *next, *prev;

public:

wcLLNode();

wcLLNode(T value, wcLLNode *next, wcLLNode *prev);

~wcLLNode() {}

};

/*********************************************************************************************/

template<class T>

class wcLinkedList {

private:

wcLLNode<T> *head; //Points to the head of the list

wcLLNode<T> *tail; //Points to the end of the list

wcLLNode<T> *cur; //Index into the list

int count; //Number of nodes in the list

public:

wcLinkedList(); //Default constructor

~wcLinkedList(); //Default destructor

void Add(T value); //Add a node to the list

int Remove(void);

int Remove(T value); //Remove a node from the list

T Next(void); //Move to the next node in the list and return its ptr

T Previous(void); //Move to the previous node in the list and return its ptr

T First(void); //Move to the first node and return its ptr

T Last(void); //Move to the last node and return its ptr

inline int Count(void) { return this->count; } //Return the number of nodes in the list

inline bool AtHead(void) { return this->cur == this->head; } //Check to see if we are at last node

inline bool AtTail(void) { return this->cur == this->tail; } //Check to see if iterator is at last node

};

Thanks,
Ayman Shoukry
VC++ Team

AymanShoukry at 2007-9-9 > top of Msdn Tech,Visual C++,Visual C++ Language...
# 6
Ayman,
Thanks for looking into this. Funny thing is, I don't even get the compile time errors you speak of. Just to try things out, I moved the constructor and destructor for wcLinkedList into the same .cpp file as my main(). I did get compile errors. I fixed these and the class was able to instantiate.
Why would it not fully compile and include my wcLinkedList.cpp file? It has the same properties and everything as all of my other files.

Weird. Please let me know if you have any ideas on this.
Cheers,
Graham

GrahamH at 2007-9-9 > top of Msdn Tech,Visual C++,Visual C++ Language...
# 7
Ayman,
One more thing. I tried copying the constructor and destructor to other .cpp files, and it doesn't work from any of them. And I have working code (no templates though) in these other files. Is there some flag or #define I have to put in to get templates to compile properly?

Thanks,
Graham

GrahamH at 2007-9-9 > top of Msdn Tech,Visual C++,Visual C++ Language...
# 8

Hi Graham: for templates to work correctly the definition of any member functions (or static data members) needs to be available in every module in which the member function (or static data member) is referenced.

For example:

// a.h
template<typename T>
class X
{
public:
X()
{
}

~X();

void mf();
};

template<typename T>
inline void X<T>::mf()
{
}

// a.cpp
#include "a.h"

class N {
};

int main()
{
X<N> *pX = new X<N>();

pX->mf();

delete pX;
}

// b.cpp
#include "a.h"

template<typename T>
X<T>::~X()
{
}

If you try to compile the above program with:

cl a.cpp b.cpp

You will get a link error because the definition of X<T>::~X() is not available when the compiler is processing a.cpp and, even though the method is referenced, the compiler cannot generate a specialization for the method. In b.cpp the compiler has the definition of X<T>::~X() but at this stage it doesn't know that the method is referenced and even if it did know it was referenced it does have access to the type N that was used to specialize the class template X.

The best approach to working with templates with Visual C++ is to ensure that the template is fully defined in every module in which it is required. This usually means fully defining the template in a header file. Take a look at the STL header files (like <vector>) that ship with Visual C++ and see how those classes are defined.

JonathanCavesMSFT at 2007-9-9 > top of Msdn Tech,Visual C++,Visual C++ Language...