dllexport универсальный класс?

Рассмотрим простой класс, который состоит только из встроенных функций-членов. Например:

template <typename T1, typename T2>
class Point2D {
public:
    typedef Point2D<T1,T2> ThisType;
    typedef T1 Tx;
    typedef T2 Ty;
    T1 x;
    T2 y;
    inline Point2D() : x(0), y(0) {}
    inline Point2D(T1 nx, T2 ny) : x(nx), y(ny) {}
    inline Point2D(const Point2D& b) : x(b.x), y(b.y) {}
    inline Point2D& operator=(const Point2D& b) { x=b.x; y=b.y; return *this; }
    inline ~Point2D() {}
};

typedef Point2D<int,int> Int2;

Когда объект типа Int2 используется в другом классе (скажем, классе MyClass, члене Int2 point), который я хочу экспортировать в DLL, я получаю следующее предупреждение:

предупреждение C4251: 'MyClass :: point': класс 'Point2D' должен иметь dll-интерфейс для использования клиентами класса 'MyClass'

Однако, если я добавлю __declspec(dllexport) в определение Point2D, как предлагает предупреждение (что мне кажется глупым, поскольку все функции встроены, плюс это шаблон, см. Вопрос SO), я получаю следующую ошибку при попытке использовать DLL в другом проекте:

ошибка LNK2019: неразрешенный внешний символ «__declspec (dllimport) public: __thiscall lwin :: Point2D :: Point2D (int, int)» ...

Обратите внимание, что определение Point2D дается в заголовке, который виден для всех проектов.

Что я должен делать? Пропустить dllexport и игнорировать предупреждение? Или есть какой-нибудь хитрый трюк, который поможет избежать путаницы с компилятором?


person CygnusX1    schedule 18.11.2012    source источник
comment
Вы получили предупреждение, потому что забыли о конструкторе копирования и операторе присваивания, которые компилятор автоматически генерирует. Просто добавьте их в класс или проигнорируйте предупреждение.   -  person Hans Passant    schedule 18.11.2012
comment
Хороший улов! Фиксированный. Но проблема остается ...   -  person CygnusX1    schedule 18.11.2012


Ответы (2)


Замените Int2 член MyClass членом Int2*, чтобы решить эту проблему. Создать экземпляр Int2 в MyClass конструкторе и удалить в MyClass деструкторе.

Шаблонный класс нельзя экспортировать, поэтому вы не можете объявить его как __declspec(dllexport). Показан C4251, поскольку размер класса контейнера может отличаться, если Dll и ее клиент скомпилированы с разными параметрами компиляции, что вызывает неопределенное поведение. С другой стороны, указатель всегда имеет одинаковый размер.

person Alex F    schedule 18.11.2012
comment
Если я заменю все Int2 на Int2* (и подобные), использую new и delete, это в конечном итоге приведет к большой потере производительности! Невозможно обойти это? - person CygnusX1; 18.11.2012
comment
К сожалению, это единственный известный мне способ решить эту проблему. Вы можете игнорировать это предупреждение, только если и Dll, и все ее клиенты всегда компилируются вместе как часть некоторой системы. Конечно, с такими же параметрами компилятора. - person Alex F; 18.11.2012
comment
@Alex: Вам это все равно нужно для экспортируемых классов. - person Ben Voigt; 18.11.2012
comment
@Ben Voigt - вы говорите об игнорировании этого предупреждения? Что ж, я никогда не делаю этого в своих проектах, возможно, этот совет был неверным. - person Alex F; 18.11.2012
comment
Без лучшего решения, если производительность критична, я бы заменил Point2D нешаблонным классом. - person Alex F; 18.11.2012
comment
Если мне это нужно для Int2, Float2, UChar2 и т. Д., То шаблон пригодится. Альтернатива - действительно уродливый макрос. Но для необходимости dllexport / dllimport я действительно не понимаю, почему макрос лучше шаблона. И шаблон, и макросы работают во время компиляции, в то время как dllexport необходим, так сказать, для связывания во время выполнения. - person CygnusX1; 19.11.2012

Попробуйте добавить __declspec(dllexport) / __declspec(dllimport) соответственно к каждому встроенному методу. Это было бы ошибкой для класса, не являющегося шаблоном, но классы / функции шаблонов, похоже, также требуют добавления этого метода для каждого метода.

person Serge Rogatch    schedule 09.09.2017