EDIT: When I merge link_def.cpp with link_dec.h, I only get the first error, not the second two.
I got these linker errors when I tried to compile some code:
CODE:
#include"list_dec.h"
#include"catch.h"
int main()
{
list<int, 100> L1;
try
{
L1.return_current();
}
catch(err)
{
return -1;
}
return 0;
}
ERRORS:
Linking...
main.obj : error LNK2019: unresolved external symbol "public: __thiscall list<int,100>::~list<int,100>(void)" (??1?$list@H$0GE@@@QAE@XZ) referenced in function __catch$_main$0
main.obj : error LNK2019: unresolved external symbol "public: int __thiscall list<int,100>::return_current(void)" (?return_current@?$list@H$0GE@@@QAEHXZ) referenced in function _main
main.obj : error LNK2019: unresolved external symbol "public: __thiscall list<int,100>::list<int,100>(void)" (??0?$list@H$0GE@@@QAE@XZ) referenced in function _main
If anyone needs the code for list_dec.h, catch.h, and list_def.cpp (the definition for the list class), just comment, I don’t want to include them if they’re irrelevent though, as they are very large.
since someone wanted to see list_dec.h (which i’ve merged with list_def.cpp for the time being)
list_dec.h:
template<class T, size_t limit>
class list
{
public:
//constructors
list();
list(const list<T, limit>& lst);
~list();
//assignment operator
void operator=(const list<T, limit>& lst);
//append new items
void append(const T& item);
//clear the list
void clear();
//return current item
T return_current();
//test if item is last item
bool last();
//make current item head of the list
void make_head();
//move to the next item
bool next();
//interrogation
size_t return_count();
size_t return_limit();
protected:
size_t count; //# of items in list
size_t current; //current item
T data[limit]; //array of elements of list
//internal functions
void copy(const list<T, limit>& lst);
};
//copier function
template<class T, size_t limit>
void list<T, limit>::copy(const list<T, limit>& lst)
{
count = lst.count;
current = lst.current;
for(size_t n = 0; n < count; n++)
{
data[n] = lst.data[n];
}
return;
}
//constructor
template<class T, size_t limit>
inline list<T, limit>::list()
{
count = 0;
current = 0;
}
//copy constructor
template<class T, size_t limit>
inline list<T, limit>::list(const list<T, limit>& lst)
{
copy(lst);
}
//assignment operator
template<class T, size_t limit>
inline void list<T, limit>::operator=(const list<T, limit>& lst)
{
clear();
copy(lst);
return;
}
//destructor
template<class T, size_t limit>
inline list<T, limit>::~list()
{
clear();
}
//append function
template<class T, size_t limit>
void list<T, limit>::append(const T& item)
{
if(count == limit)
{
throw CX_OVERFLOW;
}
data[count] = item;
count++;
return;
}
//return current item
template<class T, size_t limit>
T list<T, limit>::return_current()
{
if(count == 0)
{
throw CX_NULL;
}
if(current == count)
{
throw CX_ATEND;
}
return data[current];
}
//test if <current> pointer is at tail
template<class T, size_t limit>
inline bool list<T, limit>::last()
{
if(current == count)
{
return true;
}
else
{
return false;
}
}
//set current pointer to head
template<class T, size_t limit>
inline void list<T, limit>::make_head()
{
current = 0;
return;
}
//set current pointer to next pointer in list
template<class T, size_t limit>
bool list<T, limit>::next()
{
if(count == 0)
{
throw CX_NULL;
}
if(current == count)
{
throw CX_ATEND;
}
current++;
if(current == count)
{
return false;
}
return true;
}
//interrogation functions
template<class T, size_t limit>
inline size_t list<T, limit>::return_count()
{
return count;
}
template<class T, size_t limit>
inline size_t list<T, limit>::return_limit()
{
return limit;
}
It is possible to read linker errors with a bit of effort. Let’s give it a try:
main.obj, that is the object file generated from main.cpp.list<int,100>::~list<int,100>(void)"tells us that the problem is the destructor for the class template list, specialized for<int, 100>. This part is nicely formatted and perfectly straightforward. For the second error, we also see the return type:int __thiscall list<int,100>::return_current(void). That is, a function that returns an int, uses the __thiscall calling convention, belongs to list, is called return_current, and takes no parameters.??1?$list@H$0GE@@@QAE@XZcan pretty much be ignored. This is the name of the symbol, mangled by the compiler, and so this is the name the linker looks for inside the .obj file. But since the linker already told us the readable C++ name of the symbol, we don’t really need this. (unless you decide to go digging in the .obj file yourself).So there you have it. The first error states that the definition of the list’s destructor can not be found. If the function is not declared inline in the class definition, it should be defined in another .cpp file. I assume this is your
list_def.cpp, and that this file gets compiled and passed to the linker as well.However, this leads us to a nice little problem with templates. They are compile-time constructs, and a template has to be instantiated before the compiler emits code for it. In the output from your compiler, no code exists for the class template
list, nor for the specializationlist<int, 84>, because that specialization is not used. The compiler only generates the specializations that are actually needed.And when the compiler processes list_def.cpp, no specializations seem to be required. It can’t see into other .cpp files, like your main.cpp. It only sees the .cpp file currently being compiled, and anything it
#includes. Since it can’t seelist<int, 100>, it doesn’t generate any code for that specialization, and then when the object files are passed to the linker, it is unable to find the definition of thelist<int, 100>symbol and emits an error.The usual solution is to define all members of class templates inline in the header. That way, the definition is visible to any compilation unit including the header, and so the required templates specializations can be created by the compiler.
Specifically, the following will produce a linker error:
So will simply moving the .cpp contents into the header:
But this works, and is the usual solution
Or alternatively:
A more unusual, but occasionally useful, solution is to explicitly instantiate the template in the compilation unit that defines it. So in
list_def.cpp, add this line after the template definition:This tells the compiler specifically to generate code for that specialization, even if it is not otherwise used in this compilation unit. And obviously, this approach is only useful if you know in advance which specializations will be needed.
Edit:
It looks like you never define the
clear()function which is called from the destructor. That is the reason for the final linker error you’re getting after including everything in the header.