4

In my DLL, I have a class template and a second class derived from an instantiation of that template. Both classes shall be exported and usable in other DLLs. The compiler is Visual Studio 2013. I want the template code to be instantiated in exactly one translation unit, so I employ explicit instantiation.

The code in DLL1 is distributed as follows. Base class template:

// In BaseTemplate.h:
#pragma once

template<typename T> 
class BaseTemplate
{
public:
    T foo(T t);
};

// declare explicit instantiation
extern template class BaseTemplate < int >;    

// In BaseTemplate.cpp:
#include "BaseTemplate.h"

// template method definition
template<class T>
T BaseTemplate<T>::foo(T t)
{
    return t;
}

// explicit instantiation and export
template class __declspec(dllexport) BaseTemplate < int >;    

Derived class:

// In Derived.h:
#pragma once
#include "BaseTemplate.h"

#ifdef DLL1_EXPORTS // this is predefined in DLL1
#define DLL1_API __declspec(dllexport)
#else
#define DLL1_API __declspec(dllimport)
#endif

class DLL1_API Derived : public BaseTemplate < int >
{
public:
    void bar();
};

The theory was that the extern statement prevents instantiation in all translation units except for BaseTemplate.cpp, where explicit instantiation is performed. However, I get the following warning (which is treated as error in my project and thus breaks the build):

1> basetemplate.h(19): warning C4661: 'int BaseTemplate<int>::foo(T)' :
1> no suitable definition provided for explicit template instantiation request
1> with
1> [
1>    T=int
1> ]
1> basetemplate.h(15) : see declaration of 'BaseTemplate<int>::foo'

It seems that the export of the derived class triggers instantiation, ignoring the extern statement. If I remove the export macro from the Derived class, DLL1 compiles without warning (but other DLLs obviously will fail to link). If I aggregate a member of type BaseTemplate in class Derived instead of inheriting, it works as well (even with the export).

How can I combine explicit template instantiation and exported derived classes in Visual Studio?

Peter H
  • 475
  • 5
  • 14

1 Answers1

7

From Microsoft Support for Developers, I got the following answer to my question:

This is the current design: in this case applying ‘__declspec(dllexport)’ to the class ‘Derived’ means that all the methods of ‘Derived’ and its base classes will be exported and in order to export a method that is a member of a class template the compiler needs to instantiate the class template and all of its methods. If the definition of a method is provided elsewhere (as it is in this case) then the compiler can’t export that method – hence the warning. Note: exporting a method of a class template is more than just instantiating it – the symbol that is generated has different ‘linkage’ and a (slightly) different mangled name.

From this statement and my own experiments, I came to the following conclusion. Whenever there is an exported derived class (or class template), the implementation of the base class template must be visible for the derived class. In addition, I found that the extern statement alone does not replace the dllimport, which was missing in the sample code in my question.

It is possible to combine explicit instantiation and a header-only implementation. However, in Visual Studio you cannot avoid additional instantiations when deriving. Here is the pattern I currently employ:

In BaseTemplate.h:

#pragma once

template<typename T> 
class BaseTemplate
{
public:
    T foo(T t);
};

// Declare explicit instantiation.
#ifdef DLL1_EXPORTS // this is predefined in DLL1
    // Avoid duplicate instantation within DLL1, except for inheritance.
    extern template class BaseTemplate < int >;    
#else
    // Provide instantiation for other DLLs.
    template class __declspec(dllimport) BaseTemplate < int >;
#endif

// Include implementation in header to enable inheritance 
// (and possibly further instantiations in other DLLs).
#include "BaseTemplate_impl.h"

In BaseTemplate_impl.h:

// template method definition
template<class T>
T BaseTemplate<T>::foo(T t)
{
    return t;
}

In BaseTemplate.cpp:

#include "BaseTemplate.h"     

// explicit instantiation and export
template class __declspec(dllexport) BaseTemplate < int >;

In Derived.h:

#pragma once
#include "BaseTemplate.h"

#ifdef DLL1_EXPORTS // this is predefined in DLL1
#define DLL1_API __declspec(dllexport)
#else
#define DLL1_API __declspec(dllimport)
#endif

class DLL1_API Derived : public BaseTemplate < int >
{
public:
    void bar();
};
Peter H
  • 475
  • 5
  • 14