Reputation: 9366
I'm using templates for implementing the CRTP pattern. With the code below I'm getting linker errors (for all the methods that are defined in the base class CPConnectionBase
) like this:
error LNK2001: unresolved external symbol "public: void __thiscall CPConnectionBase::start(void)" (?start@?$CPConnectionBase@VTNCPConnection@@@@QAEXXZ)
I guess the solution here is explicit template instantiation. And indeed I can build my code when I add
#include "TNCPConnection.h"
template class CPConnectionBase<TNCPConnection>;
to the file CPConnectionBase.cpp. This is certainly the wrong place since I don't want to include the header of all possible derived classes into the source of the base class (I might want to use the base class also in another project with other derived classes).
So my goal is to instantiate the template in the source file of the derived class (TNCPConnection.h or TNCPConnection.cpp), but I couldn't find a solution. Adding
template class CPConnectionBase<TNCPConnection>;
to the file TNCPConnection.cpp does not solve my linker problems, and adding
template<> class CPConnectionBase<TNCPConnection>;
to the file TNCPConnection.cpp gives me a compile time error:
error C2908: explicit specialization; 'CPConnectionBase' has already been instantiated
How can I get rid of the linker errors without making the implementation of the base class dependent of the header files of the derived classes?
Here is the skeleton of my code:
template <class Derived>
class CPConnectionBase : public boost::enable_shared_from_this<Derived>
{
public:
void start();
};
#include "stdafx.h"
#include "CPConnectionBase.h"
template<class Derived>
void CPConnectionBase<Derived>::start()
{
...
}
#include "CPConnectionBase.h"
class TNCPConnection : public CPConnectionBase<TNCPConnection>
{
public:
void foo(void);
};
#include "stdafx.h"
#include "TNCPConnection.h"
void TNCPConnection::foo(void)
{
...
}
Upvotes: 4
Views: 7264
Reputation: 4953
First, I'd suggest renaming CPConnectionBase.cpp
to CPConnectionBase.inl
, as it doesn't contain any non-template code.
At the point where you instantiate the template, you need to #include <CPConnectionBase.inl>
first. I'd suggest doing it in TNCPConnection.cpp
.
Alternatively, you can move the CPConnectionBase
implementation to the the CPConnectionBase.h
header file, and the compiler will handle the instantiation automatically.
Upvotes: 1
Reputation: 1825
I should add a note here: MSVC allows to declare the explicit specialization before the member functions, inside the same compilation unit. GCC (4.7) requires them to be at the end of the file. I.e.
MSVC:
template class TClass<Base>;
template <class T> void TClass<T>::Function() {}
GCC:
template <class T> void TClass<T>::Function() {}
template class TClass<Base>;
Upvotes: 2
Reputation: 58931
You could also use the "Seperation Model". Just define the template in one file, and mark that definition with the keyword export:
export template <class Derived>
class CPConnectionBase : public boost::enable_shared_from_this<Derived>
{
public:
void start();
};
Upvotes: 0
Reputation: 36049
The definition of CPConnectionBase::start()
must be available at the place where you explicitly instantiate the class - otherwise the function will not be instantiated, and this non-instantiation happens silently (with linker errors following).
The standard solution is a header CPConnectionBase.hpp
that defines the template functions declared in CPConnectionBase.h
. Include CPConnectionBase.hpp
in TNCPConnection.cpp
and explicitly instantiate there.
Upvotes: 3
Reputation: 49231
move the templated code from the .cpp to the header.
When you include a header with templates, the templated code is genereated in the target code according to what it finds in the header.
If the code is in the .cpp file it can't be found and thus cant be generated (because you only included the .h)
Upvotes: 0