Как экспортировать класс, полученный из явно созданного шаблона в Visual Studio?

В моей 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();
};

Теория заключалась в том, что внешний Оператор предотвращает создание экземпляров во всех единицах перевода, кроме 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'

Кажется, что экспорт производного класса запускает инстанцирование, игнорируя внешний заявление. Если я удаляю макрос экспорта из класса Derived, DLL1 компилируется без предупреждения (но очевидно, что другие DLL не смогут связываться). Если я собираю член типа BaseTemplate в классе Derived вместо наследования, он также работает (даже с экспортом).

Как можно объединить явное создание экземпляров шаблона и экспортированные производные классы в Visual Studio?

3

Решение

От службы поддержки Microsoft для разработчиков я получил следующий ответ на мой вопрос:

Это текущий дизайн: в этом случае применение __declspec (dllexport) к классу «Производные» означает, что все методы «Производные» и его базовые классы будут экспортированы, и для экспорта метода, который является членом шаблон класса, компилятор должен создать экземпляр шаблона класса и всех его методов. Если определение метода предоставлено в другом месте (как в этом случае), то компилятор не может экспортировать этот метод — отсюда и предупреждение. Примечание: экспорт метода шаблона класса — это больше, чем просто создание его экземпляра — сгенерированный символ имеет другое «связывание» и (немного) другое искаженное имя.

Из этого утверждения и моих собственных экспериментов я пришел к следующему выводу. Всякий раз, когда существует экспортированный производный класс (или шаблон класса), реализация шаблона базового класса должна быть видимой для производного класса. Кроме того, я обнаружил, что extern одно заявление не заменяет dllimport, который отсутствовал в примере кода в моем вопросе.

Можно объединить явную реализацию и реализацию только для заголовка. Однако в 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();
};
3

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector