如何导出从 Visual Studio 中显式实例化的模板派生的类?

Pet*_*r H 4 c++ inheritance templates visual-studio explicit-instantiation

在我的 DLL 中,我有一个类模板和从该模板的实例化派生的第二个类。这两个类都应导出并可在其他 DLL 中使用。编译器是 Visual Studio 2013。我希望模板代码在一个翻译单元中被实例化,所以我使用显式实例化。

DLL1 中的代码分布如下。基类模板:

// 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 >;    
Run Code Online (Sandbox Code Playgroud)

派生类:

// 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();
};
Run Code Online (Sandbox Code Playgroud)

理论是extern语句阻止在除 BaseTemplate.cpp 之外的所有翻译单元中进行实例化,其中执行显式实例化。但是,我收到以下警告(这在我的项目中被视为错误,从而破坏了构建):

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'
Run Code Online (Sandbox Code Playgroud)

看来是导出派生类触发了实例化,忽略了extern语句。如果我从派生类中删除导出宏,DLL1 会在没有警告的情况下编译(但其他 DLL 显然将无法链接)。如果我在类 Derived 中聚合 BaseTemplate 类型的成员而不是继承,它也能正常工作(即使是导出)。

如何在 Visual Studio 中结合显式模板实例化和导出的派生类?

Pet*_*r H 6

从 Microsoft 开发人员支持中,我得到了以下问题的答案:

这是当前的设计:在这种情况下,将 '__declspec(dllexport)' 应用于类 'Derived' 意味着将导出 'Derived' 及其基类的所有方法,以便导出属于编译器需要实例化类模板及其所有方法的类模板。如果在其他地方提供了方法的定义(就像在这种情况下一样),那么编译器无法导出该方法——因此出现警告。注意:导出类模板的方法不仅仅是实例化它 - 生成的符号具有不同的“链接”和(稍微)不同的损坏名称。

从这个陈述和我自己的实验中,我得出了以下结论。每当有导出的派生类(或类模板)时,基类模板的实现必须对派生类可见。此外,我发现该extern语句本身并不能替换dllimport,而我的问题的示例代码中缺少 。

可以结合显式实例化和仅标头实现。但是,在 Visual Studio 中,您无法在派生时避免额外的实例化。这是我目前采用的模式:

在 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"
Run Code Online (Sandbox Code Playgroud)

在 BaseTemplate_impl.h 中:

// template method definition
template<class T>
T BaseTemplate<T>::foo(T t)
{
    return t;
}
Run Code Online (Sandbox Code Playgroud)

在 BaseTemplate.cpp 中:

#include "BaseTemplate.h"     

// explicit instantiation and export
template class __declspec(dllexport) BaseTemplate < int >;
Run Code Online (Sandbox Code Playgroud)

在 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();
};
Run Code Online (Sandbox Code Playgroud)