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

时间:2015-11-04 09:19:17

标签: c++ visual-studio templates inheritance 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 >;    

派生类:

// 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'

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

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

1 个答案:

答案 0 :(得分:4)

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

  

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

从这个陈述和我自己的实验中,我得出以下结论。只要存在导出的派生类(或类模板),基类模板的实现必须对派生类可见。另外,我发现仅extern语句不会替换dllimport,而我的问题中的示例代码中缺少#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"

可以组合显式实例化和仅头部实现。但是,在Visual Studio中,导出时无法避免其他实例化。这是我目前采用的模式:

在BaseTemplate.h中:

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

在BaseTemplate_impl.h中:

#include "BaseTemplate.h"     

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

在BaseTemplate.cpp中:

#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();
};

在Derived.h中:

   Thread.Sleep(1000);