Ответ 1
Несмотря на то, что в стандарте С++ нет такого требования, некоторые компиляторы требуют, чтобы все функциональные шаблоны были доступны в каждой единицы перевода, в которой он используется. Фактически для этих компиляторов тела шаблонных функций должны быть доступны в файл заголовка. Повторить: это означает, что эти компиляторы не позволят их определять в файлах без заголовка, таких как .cpp файлы. Чтобы уточнить, в С++ ese это означает, что это:
// ORIGINAL version of xyz.h
template <typename T>
struct xyz
{
xyz();
~xyz();
};
НЕ будет удовлетворен этими определениями ctor и dtors:
// ORIGINAL version of xyz.cpp
#include "xyz.h"
template <typename T>
xyz<T>::xyz() {}
template <typename T>
xyz<T>::~xyz() {}
потому что используя его:
// main.cpp
#include "xyz.h"
int main()
{
xyz<int> xyzint;
return 0;
}
приведет к ошибке. Например, с Comeau С++ вы получите:
C:\export>como xyz.cpp main.cpp C++'ing xyz.cpp... Comeau C/C++ 4.3.4.1 (May 29 2004 23:08:11) for MS_WINDOWS_x86 Copyright 1988-2004 Comeau Computing. All rights reserved. MODE:non-strict warnings microsoft C++ C++'ing main.cpp... Comeau C/C++ 4.3.4.1 (May 29 2004 23:08:11) for MS_WINDOWS_x86 Copyright 1988-2004 Comeau Computing. All rights reserved. MODE:non-strict warnings microsoft C++ main.obj : error LNK2001: unresolved external symbol xyz<T1>::~xyz<int>() [with T1=int] main.obj : error LNK2019: unresolved external symbol xyz<T1>::xyz<int>() [with T1=int] referenced in function _main aout.exe : fatal error LNK1120: 2 unresolved externals
поскольку в xyz.cpp не используется ctor или dtor, поэтому нет никаких экземпляров, которые должны произойти оттуда. К лучшему или худшему, так работают шаблоны.
Один из способов этого - явно запросить экземпляр xyz
в этом примере xyz<int>
. В результате грубой силы это можно добавить к xyz.cpp, добавив эту строку в конец:
template xyz<int>;
который запрашивает создание (все) xyz<int>
. Это не в том месте, потому что это означает, что каждый раз, когда возникает новый тип xyz, необходимо изменить файл реализации xyz.cpp. Менее интрузивным способом избежать этого файла является создание другого:
// xyztir.cpp
#include "xyz.cpp" // .cpp file!!!, not .h file!!
template xyz<int>;
Это все еще несколько больно, потому что по-прежнему требуется ручное вмешательство каждый раз, когда выдается новый xyz. В нетривиальной программе это может быть необоснованным требованием к обслуживанию.
Поэтому вместо этого другой способ приблизиться к #include "xyz.cpp"
в конец xyz.h:
// xyz.h
// ... previous content of xyz.h ...
#include "xyz.cpp"
Конечно, вы могли бы буквально принести (вырезать и вставить) содержимое xyz.cpp до конца xyz.h, отсюда избавиться от xyz.cpp; это вопрос организации файлов, и в конечном итоге результаты предварительной обработки будут одинаковыми, поскольку тела ctor и dtor будут в заголовке и, следовательно, включены в любой запрос компиляции, так как это будет использовать соответствующий заголовок. В любом случае, это имеет побочный эффект, который теперь каждый шаблон находится в вашем файле заголовка. Это может замедлить компиляцию, и это может привести к раздуванию кода. Один из способов приблизиться к последнему - объявить соответствующие функции, в этом случае ctor и dtor, как inline, поэтому для этого вам потребуется изменить xyz.cpp в примере выполнения.
В стороне некоторые компиляторы также требуют, чтобы некоторые функции были определены внутри внутри класса, а не за пределами одного, поэтому в случае этих компиляторов необходимо было бы изменить настройку выше. Обратите внимание, что это проблема компилятора, а не стандарт Standard С++, поэтому не все компиляторы требуют этого. Например, Comeau С++ не делает этого и не должен. Ознакомьтесь с http://www.comeaucomputing.com/4.0/docs/userman/ati.html для получения подробной информации о нашей текущей настройке. Короче говоря, Comeau С++ поддерживает множество моделей, в том числе тот, который близок к тому, что намерения экспорта ключевых слов (как расширение), а также даже поддерживает сам экспорт.
Наконец, обратите внимание, что ключевое слово экспорта С++ предназначено для облегчения первоначального вопроса. Однако в настоящее время Comeau С++ является единственным компилятором, который публикуется для поддержки экспорта. Подробнее см. http://www.comeaucomputing.com/4.0/docs/userman/export.html и http://www.comeaucomputing.com/4.3.0/minor/win95+/43stuff.txt. Надеюсь, что другие компиляторы достигнут соответствия стандарту С++, эта ситуация изменится. В приведенном выше примере использование экспорта означает возврат к исходному коду, который вызвал ошибки компоновщика, и внесение изменений: объявите шаблон в xyz.h с ключевым словом export:
// xyz.h
export
// ... ORIGINAL contents of xyz.h ...
ctor и dtor в xyz.cpp будут экспортироваться просто в силу #includeing xyz.h, который он уже делает. Таким образом, в этом случае вам не нужен xyztir.cpp или запрос на создание экземпляра в конце xyz.cpp, и вам не нужно вводить ctor или dtor вручную в xyz.h. В командной строке, показанной ранее, возможно, что компилятор сделает все это за вас автоматически.