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

标签 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 中组合显式模板实例化和导出的派生类?

最佳答案

从 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.

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

可以结合显式实例化和仅 header 实现。但是,在 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();
};

关于c++ - 如何导出从 Visual Studio 中显式实例化的模板派生的类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33517902/

相关文章:

javascript - 是否可以将一个通用模板用于 Handlebars.js 和 Ember.js 中的多个不同底层模型?

函数参数中的 C++ 类类型

c++ - 如何从 C++ 为 emscripten Fetch API 设置自定义 header 字段

c# - 字段启动器(静态或非静态)和构造函数(静态或非静态)首先运行

c++ - 为什么递归 constexpr 模板值不编译?

c++ - 以可变类模板作为函数调用参数的函数模板参数推导

c++ - OpenMP - 将结果存储在 vector 中

c++ - 运算符 x++;和++x;对于 int。哪个更快?为什么?

c++ - Windows 上的 Std::getline

c++ - MFC 应用程序中 Delphi 7 和 Delphi XE4 之间的 ActiveX 差异