How to make explicit template creation right?

I use templates to implement a CRTP template. With the code below, I get linker errors (for all methods that are defined in the CPConnectionBase base class) as follows:

error LNK2001: unresolved external character "public: void __thiscall CPConnectionBase :: start (void)" (? start @? $ CPConnectionBase @VTNCPConnection @@@@ QAEXXZ)

I think the solution here is to explicitly create a template. And indeed, I can create my code when I add

 #include "TNCPConnection.h" template class CPConnectionBase<TNCPConnection>; 

to the CPConnectionBase.cpp file. This, of course, is the wrong place, because I do not want to include the header of all possible derived classes in the source code of the base class (I could use the base class in another project with other derived classes).

So, my goal is to create an instance of the template in the source file of the derived class (TNCPConnection.h or TNCPConnection.cpp), but I could not find a solution. Adding

 template class CPConnectionBase<TNCPConnection>; 

to the TNCPConnection.cpp file does not solve my problems with the linker and adds

 template<> class CPConnectionBase<TNCPConnection>; 

to the TNCPConnection.cpp file gives me a compile-time error:

bug C2908: explicit specialization; "CPConnectionBase" has already been created

How can I get rid of linker errors without making the base class implementation dependent on the derived class header files?

Here is the skeleton of my code:

CPConnectionBase.h

 template <class Derived> class CPConnectionBase : public boost::enable_shared_from_this<Derived> { public: void start(); }; 

CPConnectionBase.cpp

 #include "stdafx.h" #include "CPConnectionBase.h" template<class Derived> void CPConnectionBase<Derived>::start() { ... } 

TNCPConnection.h

 #include "CPConnectionBase.h" class TNCPConnection : public CPConnectionBase<TNCPConnection> { public: void foo(void); }; 

TNCPConnection.cpp

 #include "stdafx.h" #include "TNCPConnection.h" void TNCPConnection::foo(void) { ... } 
+4
source share
5 answers

The definition of CPConnectionBase::start() should be available in the place where you explicitly create an instance of the class, otherwise the function will not be created, and this non-instantiation occurs silently (with subsequent linker errors).

The standard solution is the CPConnectionBase.hpp header, which defines the template functions declared in CPConnectionBase.h . Include CPConnectionBase.hpp in TNCPConnection.cpp and explicitly create an instance there.

+3
source

I have to add a note: MSVC allows you to declare explicit specialization in front of member functions inside the same compilation unit. GCC (4.7) requires that they 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>;

+2
source

First, I suggest renaming CPConnectionBase.cpp to CPConnectionBase.inl , since it does not contain any template without templates.

At the moment you create the template, you first need #include <CPConnectionBase.inl> . I suggest doing this in TNCPConnection.cpp .

Alternatively, you can move the CPConnectionBase implementation to the CPConnectionBase header file, and the compiler will automatically process the instantiation.

+1
source

move the boilerplate code from .cpp to the header.

When you include a header with templates, the template code is generated in the target code according to what it finds in the header.

If the code is in the .cpp file, it cannot be found and therefore can be generated (because you only included .h)

0
source

You can also use the "Seperation Model" . Just define the template in one file and note that the definition with the export keyword is:

 export template <class Derived> class CPConnectionBase : public boost::enable_shared_from_this<Derived> { public: void start(); }; 
0
source

Source: https://habr.com/ru/post/1394154/


All Articles