Экспорт шаблонов или как испортить жизнь коллегам
Предположим, вам требуется написать разделяемую библиотеку (DLL в терминологии Windows и shared library в мире *nix). Допустим, в библиотеке реализована некоторая фабричная функция, которая создаёт очень полезные для пользователей объекты.
Примерно так:
#if defined(_MSC_VER) // Microsoft #define EXPORT __declspec(dllexport) #define IMPORT __declspec(dllimport) #elif defined(__GNUC__) // GCC #define EXPORT __attribute__((visibility("default"))) #define IMPORT #else // do nothing and hope for the best? #define EXPORT #define IMPORT #pragma warning Unknown dynamic link import/export semantics. #endifstd::shared_ptr<SomeUsefulClass> create();
В чем же проблема?
Проблема в двойном инстанцировании шаблона std::shared_ptr<SomeUsefulClass>. Первое произойдёт непосредственно при компиляции вашей библиотеки, а второе – в момент компиляции пользовательского кода, который будет использовать библиотеку.А теперь самое главное – никто не может гарантировать, что эти две результирующие версии, казалось бы, одного и того же шаблона будут одинаковыми. Стандарт C++ описывает только интерфейс класса std::shared_ptr, но не детали реализации.
Если библиотека и пользовательский код были собраны разными компиляторами (или даже разными версиями одного и того же компилятора), могут возникнуть проблемы. Можно подумать, что основная проблема – недостаточно полная формулировка стандарта в части описания деталей std::shared_ptr. Но это не совсем так.
Ещё одна проблема заключается в том, что бинарная совместимость гарантируется только для standart-layout типов данных. Только такие типы можно без опаски использовать на стыке между кодом, который может быть собран разными компиляторами (или разными версиями).
Почему бы стандарту C++ не дать больше гарантий?
Причина всё та же – эффективность. Чем меньше ограничений накладывает стандарт языка, тем больше оптимизаций может выполнить компилятор.Что почитать подробнее:
StandardLayoutType_2
StandardLayoutType_2
Остались вопросы? Пишите в комментариях!