вам нужно отдельная отметка каждый шаблон с API
(__declspec(dllexport)
или __declspec(dllimport)
) и не указывать его в коде класса.
Файл Foo.h:
#ifdef _DLL
#define API __declspec(dllexport)
#else
#define API __declspec(dllimport)
#endif
class API Foo
{
public:
template<typename T>
API static T& Get();
static void Set();
};
обратите внимание, что мы разделяем метку Get()
на API
, несмотря на то, что все классы Foo
также помечены API
(метка класса фактически не влияет на функцию шаблона, поэтому нужно пометить ее отдельно). и никакой реализации Get
здесь - экспортируемые функции в любом случае не могут быть встроенными.
поэтому код dll (Foo.cpp) должен выглядеть так:
#include "foo.h"
template<typename T>
API T& Foo::Get()
{
__pragma(message("__imp_" __FUNCDNAME__)) // for debug
static T _instance;
return _instance;
}
void Foo::Set()
{
Foo::Get<int>() = 10;
}
обратите внимание, что мы снова явно используем API
(__declspec(dllexport)
) в реализации тела функции. это очень важно - компилятор не предупреждает вас, если вы пропустите API
здесь, но без этого - Get
не будет экспортировано.
точно, что на данный момент все правильно - скопируйте строку, созданную __pragma(message("__imp_" __FUNCDNAME__))
(она будет выглядеть как __imp_??$Get@H@Foo@@SAAEAHXZ
) и выполните поиск точно (от символа к символу) этого строка в созданном файле .lib - после сборки dll. если он существует - все в порядке, иначе бессмысленно продолжать (с exe)
и в exe:
#include "../foo_dll/foo.h"
Foo::Get<int>() = 3;
Foo::Set();
if (Foo::Get<int>() != 10)
{
__debugbreak();
}