What do these communication errors mean? (C ++) (MSVC ++)
EDIT: When I merge link_def.cpp with link_dec.h, I only get the first error, not the second.
I got these linker errors when I tried to compile the 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 are out of date though, since they are very large.
since someone wanted to see list_dec.h (to which I have now merged with list_def.cpp) 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;
}
With errors, you can see linker errors. Let's try to try:
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
- The error occurs in
main.obj
, that is, the object file generated from main.cpp. - The problem is an "unresolved external symbol". That is, you are referring to a symbol that is expected to be defined in another compilation unit, but the linker cannot find that definition.
- This symbol is a public member function (__thiscall is a call for member functions).
- Now we get to the really useful information:
list<int,100>::~list<int,100>(void)"
reports that the problem is the class template list destructor specialized for<int, 100>
. This part is well formatted and downright simple. 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 a list, is called return_current, and takes no parameters. -
??1?$list@H$0GE@@@QAE@XZ
can be largely ignored. This is the name of the symbol mangled by the compiler, and therefore is the name that the linker looks for inside the .obj file. But since the linker already told us the readable name of the C ++ symbol, we don't really need that. (unless you want to dig into the .obj file yourself). - Finally, this tells us that the symbol is referenced in main. (A general rule of thumb when reading linker errors is to ignore bits you don't understand. And I'm not really sure what catch means here, but it has to do with the exception handling you do inside the main one.
So there you have it. The first error states that the definition of the list destructor cannot be found. If a function is not declared inline in a class definition, it must be defined in another .cpp file. I assume this is yours list_def.cpp
and this file is compiled and passed to the linker.
However, this brings us to a small problem with templates. They are compile-time constructs, and a template must be created before the compiler emits code for it. There is no code for a class template list
or for a specialization in the output of your compiler list<int, 84>
because that specialization is not used. The compiler only generates the required specializations.
And when the compiler processes list_def.cpp, no specializations are required. It cannot see in other .cpp files like main.cpp. It only sees the compiled .cpp file and whatever it #include
s. Since it cannot see list<int, 100>
, it does not generate any code for this specialization, and then when the object files are passed to the linker, it cannot find the definition of the symbol list<int, 100>
and emits an error.
The usual solution is to define all members of inline class templates in the header. Thus, the definition is visible to any compilation unit, including the header, and therefore the required compilers can create specialized specializations.
Specifically, the following will result in a linker error:
// .h
template <int n>
class Foo {
int Bar();
};
// .cpp
template <int n>
int Foo::Bar() {
return n; // Error: This is not visible from other .cpp files
}
So, just move the content of the .cpp to the header:
// .h
template <int n>
class Foo {
int Bar();
};
template <int n>
int Foo::Bar() {
return n; // Error: This is will cause the function to be defined in every .cpp file that includes it, so you'll get a *different* linker error instead (multiple definitions)
}
But it works and is a common solution
// .h
template <int n>
class Foo {
int Bar() { return n; } // just define it here, inside the class definition, and it is implicitly inline, so it ok that multiple .cpp files see it
};
Or alternatively:
// .h
template <int n>
class Foo {
int Bar();
};
// still in .h
template <int n>
inline int Foo::Bar() { // explicitly marking it inline works too. Now the compiler knows that it might be defined in multiple .cpp files, and these definitions should be merged back together
return n;
}
A more unusual, but sometimes useful solution is to explicitly instantiate the template in the compilation unit that defines it. So list_def.cpp
add this line after the template definition:
template class list<int, 100>;
This directs the compiler to specifically generate code for this specialization, even if it is not used in that compiler. And obviously, this approach is useful if you know in advance what specializations are needed.
Edit:
It looks like you never define a function clear()
that is called from the destructor. This is the reason for the last linker error you get after including everything in the header.
a source to share
My intuition tells me that you are probably not declaring template specialization properly. If your implementation of a list class is generic, you need to tell the compiler what types you will use it with. Just put the line
template class list<int, 100>;
at the bottom of the list, list_def.cpp will most likely solve your problem.
a source to share
In my programming experience, when I have a templated class, I follow the following
#ifndef MY_LIST
#define MY_LIST
class List< [...] >
{
[ ...]
};
#include "MyListImplementation.h"
#endif
The actual implementation code also ends up in the ".h" file, not the CPP file, I don't know exactly why this is happening, but I know you can make it work this way.
a source to share