Archived

This topic is now archived and is closed to further replies.

CGameProgrammer

Errors with Member Functions of Template Classes

Recommended Posts

This is an example of the kind of error that occurs. It's really annoying me.
        
//// EXAMPLE.H ////

template <class T> struct Example
{
    Example <T>( );
};
////////////////

 
//// EXAMPLE.CPP ////

#include "Example.h"
 
template <class T> Example <T> :: Example <T> ( )
{
}
 
/* INTERESTING NOTE: This version of the code works fine
Example<int> :: Example<int> ( )
{
}
*/
/////////////////////

 
//// MAIN.CPP ////

#include "Example.h"
 
void main ( )
{
    Example<int> E;
}
/////////////////////

         
Main.obj makes a linker error "unresolved external symbol: public: __thiscall Example<int>::Example<int>(void)". The error does not occur if the constructor definition is placed in MAIN.CPP or EXAMPLE.H, but when called from a different file from which it's defined in, it makes the linker error. Is there anyway to fix this, besides moving the location of the constructor? ~CGameProgrammer( );

Edited by - CGameProgrammer on January 3, 2002 1:20:15 PM

Share this post


Link to post
Share on other sites
JonStelly    127
Ah, you added the example at the same time that I first replied. I didn''t know you were working with template classes / structures. Template member functions are a bit odd... Look up Q239436 in MSDN. Basically, the easiest / cleanest thing to do is to define the member functions where you declare them (types.h) and get rid of types.cpp.

Share this post


Link to post
Share on other sites
This is perfectly normal. When you define a template class in C++, you are not defining a class. You are rather giving the compiler a "model" that it can use to create a whole family of classes. For example, a templated queue class does not really define a queue, but it tells the compiler how to define queues of chars or queues of ints etc.

Let''s see what happens in your particuar case...

//// EXAMPLE.H ////
template struct Example
{
Example();
};

//// EXAMPLE.CPP ////
#include "Example.h"

template Example::Example()
{
//...
}

//// MAIN.CPP ////
#include "Example.h"

void main()
{
Example E;
}


When the compiler sees Example''s template definition (in Example.h), it doesn''t know which type the template is gonna be used with for a particular program. Since the possibilities are endless, the compiler can''t compile the template if it finds it out of context. It will make sure the syntax is ok, but it can''t generate code because it doesn''t have enough information to do that...

In Main.cpp, let''s look at the following statement:

Example E;

This tells the compiler to instantiate the Example template class for ints (let''s call this "Example of ints"). Only at that particular time the compiler has the information it needs to generate the code for an "Example of ints"... But the problem is that when Example.cpp was compiled, the "Example of ints" wasn''t instantiated yet because the compiler didn''t know which type was gonna be used with the template. Furthermore, the "Example of ints" wasn''t instanciated when Main.cpp was compiled because the compiler had no access to the source code for Example.cpp when it was compiling Main.cpp!

-------------------------
NOTE:
Some compilers automatically instantiate template classes, by using a "smart" linker that calls the compiler on demand to create the needed classes, but this is more trouble than most compiler writers are willing to take.
-------------------------

There are 2 solutions to this problem:

1) Put all your implementation of Example.cpp in Example.h, like in the follwing example:

//// EXAMPLE.H ////

#ifndef EXAMPLE_H //Inclusion guards are always good...
#define EXAMPLE_H

//Definition of the Example class template
template struct Example
{
Example();
void DoSomething();
void DoSomethingElse();
};

//Implementation of the Example class template
template Example::Example()
{
//...
}

template void Example::DoSomething()
{
//...
}

template void Example::DoSomethingElse()
{
//...
}

#endif



2) "Include" your implementation file at the end of the header file, like in the following example:

//// EXAMPLE.H ////

#ifndef EXAMPLE_H //Inclusion guards are always good...
#define EXAMPLE_H

//Definition of the Example class template
template struct Example
{
Example();
void DoSomething();
void DoSomethingElse();
};

#include "Example.cpp"
#endif

//// EXAMPLE.CPP ////

#include "Example.h"

//Implementation of the Example class template
template Example::Example()
{
//...
}

template void Example::DoSomething()
{
//...
}

template void Example::DoSomethingElse()
{
//...
}

Share this post


Link to post
Share on other sites
Dobbs    164
The feature you''re talking about is part of the ISO C++ standard, but no compilers that I know of actually support it. You''re supposed to be able to declare a template class in a header like any other class, then define the implementation of member functions elsewhere using the export keyword.

Share this post


Link to post
Share on other sites