views:

349

answers:

6

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;
}
A: 

Are you sure you're really compiling and linking with list_def.cpp (where I imagine your list<> template is defined)? The linker can't find any of the three members your are using (destructor, return_current() or default constructor.

Pontus Gagge
list_def.cpp includes list_dec.h within it. When I add '#include "list_def.cpp"' I get a huge list of linking errors, but I'll ty merging list_def.cpp and list_dec.h and see what happens.
Keand64
Merging them into one .h file sort of worked, but I still get the first error (the destructor error).
Keand64
A: 

My intuition tells me you're probably not declaring the template specialization properly. If your list class implementation is generalized then you need to tell the compiler what types you will be using it with. Just putting the line

template class list<int, 100>;

at the bottom of list_def.cpp will then most likely solve your problem.

codebolt
I used "template<class T, size_t limit>", then "class list{//stuff};"I'll try changing the second line (class list) to "class list<class T, size_t limit>" or "class list<T, limit>"
Keand64
Nope. changing it just gives me more errors.
Keand64
A: 

In my programming experience, whenever 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 goes in a ".h" file not a CPP file, I'm not sure exactly why this happens, but I know that this way you can get it to work.

ferrari fan
This is because the template method implementations must be visible at each point of USAGE of the template because the method is really compiled there. The usage is the point when the template parameters are known; so the compiler can compile the real assembler code from C++ only at that point of parsing. And to do so it needs to know the implementation there. If it lies in the .cpp file it could only see the declaration...
rstevens
A: 

Did you implement the methods of the template class list into the list_dec.h or in the list_dec.cpp?

If not you should move them because template method implementations must always be visible by all points of usage.

rstevens
Not quite true - the template must have been instantiated prior to link time, but it doesn't have to be instantiated in each translation unit. Like codebolt said, you can declare any specializations at the bottom of a .cpp implementation and your compiler will instantiate them.
mskfisher
Yes you're right...but in Keand64's case this would mean that he has to add an implementation for list<int, 100> into the list_dec.cpp. And I don't think that the list class would be a very useful re-usable template class if you do so.
rstevens
+5  A: 

It is possible to read linker errors with a bit of effort. Let's give it a 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 reference a symbol that is expected to be defined in another compilation unit, but the linker can not find this definition.
  • The symbol in question is a public member function (__thiscall is the calling convention for member functions).
  • Now we come to the really useful information: 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@XZ can 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).
  • Finally, it tells us that the symbol is referenced in the function main. (The general rule when reading linker errors is to ignore the bits you don't understand. And I'm not entirely sure what the "catch" part here means, but it's tied to the exception handling you do inside main.

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 specialization list<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 see list<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 the list<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:

// .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 will simply moving the .cpp contents into 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 this works, and is the usual 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's 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 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:

template class list<int, 100>;

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.

jalf
+1  A: 

Another hunch: how have you defined the class template's member functions? Are they inlined? Do you have the definition in a separate implementation file? If you do, you may be running into this issue: FAQ 35.16.

dirkgently