如何导出从 Visual Studio 中显式实例化的模板派生的 class?
How to export a class derived from an explicitly instantiated template in Visual Studio?
在我的 DLL 中,我有一个 class 模板和从该模板的实例派生的第二个 class。两个 classes 都应导出并可在其他 DLL 中使用。编译器是Visual Studio 2013。我希望模板代码在一个翻译单元中实例化,所以我使用显式实例化。
DLL1中的代码分布如下。
基本 class 模板:
// 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 >;
派生 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();
};
理论是 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'
好像是派生class的export触发了实例化,忽略了extern语句。如果我从 Derived class 中删除导出宏,DLL1 会在没有警告的情况下编译(但其他 DLL 显然会失败 link)。如果我在 class 中聚合一个 BaseTemplate 类型的成员而不是继承,它也能正常工作(即使导出)。
如何在 Visual Studio 中组合显式模板实例化和导出的派生 classes?
从 Microsoft 开发人员支持中心,我得到了以下问题的答案:
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.
根据这个陈述和我自己的实验,我得出以下结论。每当存在导出的派生 class(或 class 模板)时,基本 class 模板的实现必须对派生 class 可见。此外,我发现 extern
语句本身并不能替换 dllimport
,我的问题中的示例代码中缺少它。
可以结合显式实例化和 header-only 实现。但是,在 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"
在BaseTemplate_impl.h中:
// template method definition
template<class T>
T BaseTemplate<T>::foo(T t)
{
return t;
}
在BaseTemplate.cpp中:
#include "BaseTemplate.h"
// explicit instantiation and export
template class __declspec(dllexport) BaseTemplate < int >;
在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();
};
在我的 DLL 中,我有一个 class 模板和从该模板的实例派生的第二个 class。两个 classes 都应导出并可在其他 DLL 中使用。编译器是Visual Studio 2013。我希望模板代码在一个翻译单元中实例化,所以我使用显式实例化。
DLL1中的代码分布如下。 基本 class 模板:
// 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 >;
派生 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();
};
理论是 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'
好像是派生class的export触发了实例化,忽略了extern语句。如果我从 Derived class 中删除导出宏,DLL1 会在没有警告的情况下编译(但其他 DLL 显然会失败 link)。如果我在 class 中聚合一个 BaseTemplate 类型的成员而不是继承,它也能正常工作(即使导出)。
如何在 Visual Studio 中组合显式模板实例化和导出的派生 classes?
从 Microsoft 开发人员支持中心,我得到了以下问题的答案:
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.
根据这个陈述和我自己的实验,我得出以下结论。每当存在导出的派生 class(或 class 模板)时,基本 class 模板的实现必须对派生 class 可见。此外,我发现 extern
语句本身并不能替换 dllimport
,我的问题中的示例代码中缺少它。
可以结合显式实例化和 header-only 实现。但是,在 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"
在BaseTemplate_impl.h中:
// template method definition
template<class T>
T BaseTemplate<T>::foo(T t)
{
return t;
}
在BaseTemplate.cpp中:
#include "BaseTemplate.h"
// explicit instantiation and export
template class __declspec(dllexport) BaseTemplate < int >;
在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();
};