关于包含静态成员变量的 C++ 模板类以及从动态库或共享对象中导出它们的问题有很多。但是这个更深一点:如果有多个共享对象,每个共享对象都有自己的一组实例化但可能使用来自另一个共享对象的实例化,该怎么办?
考虑以下示例代码:
/* file: common.h */
#include <stdio.h>
#define PRINT fprintf (stderr, "(template %d) %d -> %d\n", parameter, data, new_data)
template <int parameter>
class SharedClass
{
static int data;
public:
static void Set(int new_data) { PRINT; data = new_data; }
};
template <int parameter>
int SharedClass<parameter>::data = parameter;
/* file: library1.h */
extern template class SharedClass<1>;
void Library1Function();
/* file: library1.cpp */
#include "common.h"
#include "library1.h"
#include "library2.h"
template class SharedClass<1>;
void Library1Function()
{
SharedClass<1>::Set (100);
SharedClass<2>::Set (200);
}
/* file: library2.h */
extern template class SharedClass<2>;
void Library2Function();
/* file: library2.cpp */
#include "common.h"
#include "library1.h"
#include "library2.h"
template class SharedClass<2>;
void Library2Function()
{
SharedClass<1>::Set (1000);
SharedClass<2>::Set (2000);
}
/* file: main.cpp */
#include "common.h"
#include "library1.h"
#include "library2.h"
int main()
{
Library1Function();
Library2Function();
SharedClass<1>::Set (-1);
SharedClass<2>::Set (-2);
}
然后假设我们使用 GCC 构建了两个库和一个应用程序:
$ g++ -fPIC -fvisibility=default -shared library1.cpp -o lib1.so
$ g++ -fPIC -fvisibility=default -shared library2.cpp -o lib2.so
$ g++ -fvisibility=default main.cpp -o main -Wl,-rpath=. -L. -l1 -l2
然后运行可执行文件,我们将得到以下结果:
$ ./main
(template 1) 1 -> 100
(template 2) 2 -> 200
(template 1) 100 -> 1000
(template 2) 200 -> 2000
(template 1) 1000 -> -1
(template 2) 2000 -> -2
这意味着库和可执行文件都访问相同的每个模板静态存储。
如果我们在二进制文件上运行“nm -C”,我们会看到每个静态成员只定义一次并且在相应的库中:
$ nm -C -A *.so main | grep ::data
lib1.so:0000000000001c30 u SharedClass<1>::data
lib2.so:0000000000001c30 u SharedClass<2>::data
但我有一些问题。
为什么,如果我们
extern template class ...
从两个头文件中删除,我们会看到静态成员存在于每个二进制文件中,但测试应用程序将继续正常工作?$ nm -C -A *.so main | grep ::data lib1.so:0000000000001c90 u SharedClass<1>::data lib1.so:0000000000001c94 u SharedClass<2>::data lib2.so:0000000000001c94 u SharedClass<1>::data lib2.so:0000000000001c90 u SharedClass<2>::data main:0000000000401e48 u SharedClass<1>::data main:0000000000401e4c u SharedClass<2>::data
是否可以在 MSVC 下构建它?
或者,更具体地说,如何处理__declspec(dllexport)
和__declspec(dllimport)
使一些实例化导出,而一些实例化 - 导入?最后:这是未定义行为的一个例子吗?