Skip to content

显式实例化解决模板导出静态库动态库

前言

我们使用显式实例化解决模板导出动态静态库这个问题,原因什么的不再过多赘述,总结一句话:

  • 模板需要实例化才能生成实际的代码,既然不能隐式实例化,那就显式实例化

我们以 windows 环境 VS2022sln 解决方案为例。

不再使用 cmake 主要是本课程也不是教你 cmake 的,这些东西有点麻烦,不如直接用 VS 按钮点点点设置就是。之前用 cmake 项目主要在于 add_executable,比较明确,说明编译哪些文件。

我们创建了一个解决方案,其中有四个项目:

  1. 测试动态库使用
  2. 测试静态库使用
  3. 用模板生成动态库
  4. 用模板生成静态库

生成动态库和静态库用的代码几乎是一模一样的,只是去掉了 __declspec(dllexport)

测试动态库和静态库使用的主要区别在于项目配置,代码上的区别是去掉 __declspec(dllexport)

模板生成动态库与测试

export_template.h

cpp
#pragma once

#include <iostream>
#include <string>

template<typename T>
void f(T);

template<typename T>
struct __declspec(dllexport) X {
    void f();
};

export_template.cpp

cpp
#include "export_template.h"

template<typename T>
void f(T) {                                 // 函数模板定义
    std::cout << typeid(T).name() << '\n';
}

template <typename T>
void X<T>::f(){                             // 类模板中的成员函数模板定义
    std::cout << typeid(T).name() << '\n';
}

template __declspec(dllexport)  void f<int>(int);
template __declspec(dllexport)  void f<std::string>(std::string);

template struct X<int>;     // 类模板显式实例化

这很简单,和之前分文件写法的区别只是用了__declspec(dllexport)

可以使用 __declspec(dllexport) 关键字从 DLL 中导出数据、函数、类或类成员函数。

以上示例中的函数模板、类模板显式实例化,不要放在 .h 文件中,因为 一个显式实例化定义在程序中最多只能出现一次;如果放在 .h 文件中,被多个翻译单元使用,就会产生问题。

当显式实例化函数模板、变量模板 (C++14 起)、类模板的成员函数或静态数据成员,或成员函数模板时,只需要它的声明可见

类模板、类模板的成员类或成员类模板在显式实例化之前必须出现完整定义,除非之前已经出现了拥有相同模板实参的显式特化

我们将生成的 .dll.lib 文件放在了指定目录下,配置了项目的查找路径以供使用。

run_test.cpp.cpp

cpp
#include "export_template.h"
#include <string>

int main(){
    std::string s;
    f(1);
    //f(1.2); // Error!链接错误,没有这个符号
    f(s);
    X<int>x;
    x.f();
}

模板生成静态库与测试

没有多大的区别,原理都一样,代码也差不多,不必再讲。

总结

我们没有讲述诸如:项目如何配置,怎么配置项目生成动态库、静态库,这也不是我们的重点。在视频中我们会从头构建这些。但是文本的话,不必要从头教学这些基本知识。

我们关注代码上即可。

静态库也没有单独提,因为的确没啥区别。

另外如果你直接打开我们的项目,无法编译或许很正常,请自己根据当前环境,处理编码问题,以及生成动态静态库,配置,使用。