
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.97.9.173] |
![]() |
|
Сообщ.
#1
,
|
|
|
Товарищ Железный Феликс (aka Flex Ferrum), по моим оценкам ответил раз тридцать на этот вопрос, и, тем не менее, вопрос этот периодически возникает. Чаще всего его задают новички, но иногда и опытные программисты попадают в этот капкан.
Кому не в лом, с обсуждением этой темы и предложенными решениями можно ознакомиться по следующим, напр. ссылкам: шаблонный класс Проблема с шаблоном шаблонные классы Шаблонные функции Шаблоны (templates) в С++ LNK ошибка при шаблонном наследовании шаблонные члены класса ошибки при линковке библиотк с шаблонами шаблоны error LNK2019: unresolved external symbo Вопрос по шаблонам template's [список может быть продолжен] Итак, проблема: имеем в проекте, к примеру три файла file.h, file.cpp, main.cpp /* file.h - описание шаблона */ ![]() ![]() template <class T> class C { public : C (void); private : T t; }; /* file.cpp - определение функций шаблона */ ![]() ![]() #include "file.h" template <class T> C<T> :: C (void): t (1) {;} /* main.cpp - использование шаблона */ ![]() ![]() #include "file.h" C<float> c; int main (int, char *[]) { return 0; } Файлы file.cpp и main.cpp транслируются раздельно. Пытаемся собрать проект, - и на этапе компоновки линкер нам сообщает о том, что какой-то symbol оказался unresolved. Сообщение об этой ошибке может зависеть от среды разработки, но в общем суть его одна. В чём же тут проблема ? А проблема в том, дорогие товарищи, что в Европе кофе не растёт. Большинство наиболее распространённых и наиболее популярных компиляторов не поддерживает это средство языка, которое называется экспорт шаблонов. В их числе: - Компиляторы Borland C++ (все версии всех годов, правда про Builder 2006 не могу точно сказать, но скорее всего и он тоже не поддерживает); - Microsoft C++ (тоже самое, ни один из них не поддерживает); - GNU gcc/g++ (тоже ни одна из версий не поддерживает); Менее распространённые, но которые мне проходилось использовать: - QNX qcc (все версии); Компиляторы для встраиваемых приложений: - IAR EWB (все поддерживаемые процессорные ядра/устройства); - Green Hills Multi2000; - Analog Devices Visual DSP++ (все версии); - Texas Instruments Code Composer; [список может быть продолжен] В некоторых средах разработки, например таких как Microsoft VS, Green Hills, или GNU gcc об этом явно сообщается в документации. Из мне известных компиляторов, которые могут осилить такой код, могу назвать только - Intel C++ (версии, выпущеные позже октября 2004 г.) [список может быть продолжен] Как решать эту проблему ? Наиболее простой ответ: на этапе трансляции описание шаблона, определение шаблона, и его использование должно находится в одной единице трансляции. На примере вышеприведённого кода можно сделать так : /* file.h - описание шаблона */ ![]() ![]() template <class T> class C { public : C (void) : t (1) {;} private : T t; }; /* main.cpp - использование шаблона */ ![]() ![]() #include "file.h" C<float> c; int main (int, char *[]) { return 0; } определение шаблонного метода выкидываем из file.cpp или вот так, если в лом переписывать заголовок: /* file.h - описание шаблона */ ![]() ![]() template <class T> class C { public : C (void); private : T t; }; /* file.cpp - определение функций шаблона */ ![]() ![]() #include "file.h" template <class T> C<T> :: C (void): t (1) {;} /* main.cpp - использование шаблона */ ![]() ![]() #include "file.h" #include "file.cpp" /* брррр...... ахтунг, но работать будет*/ C<float> c; int main (int, char *[]) { return 0; } второй вариант: частичная специализация. Если известо, какой тип будет параметром заполнения шаблона, то можно явно сгенерить его методы в отдельной единице трансляции: /* file.h - описание шаблона */ ![]() ![]() template <class T> class C { public : C (void); private : T t; }; template <> C <float> :: C (void); /* file.cpp - определение функций шаблона */ ![]() ![]() #include "file.h" template <> C<float> :: C (void): t (3.1415) {;} /* main.cpp - использование шаблона */ ![]() ![]() #include "file.h" C<float> c; int main (int, char *[]) { return 0; } Такой вариант тоже работоспособен. В догон. Какие шаблоны практически удаётся скомпилять в раздельных единицах трансляции? С применением вышеупомянутого Intel C++ мне удалось скомпилировать шаблонную функцию объявленную в одном файле, а определённую в другом. Как это выглядит (icc 8.1, Linux FC2) /* file.cpp - определение функций шаблона */ ![]() ![]() #include <stdio.h> #include <typeinfo> export template <class T> T f (T a) { printf ("type of v : %s\n", typeid (a).name ()); return a + 1; } /* main.cpp - использование шаблона */ ![]() ![]() export template<class T> T f (T); float s = 3.1415; double d = 2.71828; const char * c = "hello world\n"; int main () { f (s); f (d); f (c); return 0; } командная строка: [user@work template]$ icpc -export -export_dir ./ -c file.cpp [user@work template]$ icpc -export -export_dir ./ -c main.cpp [user@work template]$ icpc -export -export_dir ./ file.o main.o -o test [user@work template]$ ./test type of v : f type of v : d type of v : PKc ну и так должен выглядеть правильно код, приведённый в самом начале темы, что бы он был правильно понят компилятором: /* file.h - описание шаблона */ ![]() ![]() template <class T> class C { public : C (void); private : T t; }; export template <class T> C <T> :: C (void); /* file.cpp - определение функций шаблона */ ![]() ![]() #include <stdio.h> #include <typeinfo> export template <class T> C<T> :: C (void) : t (1.4142) { t += 1; printf (" v = %f, type = %s\n", t, typeid (t).name ()); } /* main.cpp - использование шаблона */ ![]() ![]() #include "file.h" C<float> c; int main (int, char *[]) { return 0; } командная строка : [user@work template]$ icpc -export -export_dir ./ -c file.cpp [user@work template]$ icpc -export -export_dir ./ -c main.cpp [user@work template]$ icpc -export -export_dir ./ file.o main.o -o test [user@work template]$ ./test v = 2.414200, type = f Да только, как уже было показано выше, такой код очень немногим компиляторам по зубам. Успехов всем! |
Сообщ.
#2
,
|
|
|
Цитата antigen @ Из мне известных компиляторов, которые могут осилить такой код, могу назвать только - Intel C++ (версии, выпущеные позже октября 2004 г.) [список может быть продолжен] Я бы отнесстя к поддержке такой возможности весьма скептически... Может, лишь самые простейшие случаи... |
Сообщ.
#3
,
|
|
|
Цитата BioUnit @ Я бы отнесстя к поддержке такой возможности весьма скептически... Может, лишь самые простейшие случаи... Совершенно верно. Хелловорлд работает, а чо чуть посложнее - уже не компилируется. Возможно в более свежих весриях эта поддержка будет улучшена. |
Сообщ.
#4
,
|
|
|
Цитата antigen @ второй вариант: частичная специализация. Если известо, какой тип будет параметром заполнения шаблона, то можно явно сгенерить его методы в отдельной единице трансляции: /* file.h - описание шаблона */ ![]() ![]() template <class T> class C { public : C (void); private : T t; }; template <> C <float>::C(void); template <> C <float>::C(void) это НЕ частичная специализация, а явная(полная) специализация члена(конструктора в данном случае). Более красиво вариант 2 реализуется директивой явного инстанцирования. Обобщю немного сказанное, существует несколько способов организации исходного кода шаблонов: 1. Наиболее популярный подход - модель включения(inclusion model), определения шаблонов полностью размещаются в заголовочном файле. 2. Модель явного инстанцирования(explicit instantiation model), как правило реализуется директивой явного инстанцирования (explicit instantiation directive). 3. Модель разделения шаблонов(separation model), реализуется с помощью export. |
Сообщ.
#5
,
|
|
|
Почему разработчики компиляторов не торопятся с поддержкой экспорта шаблонов. Герб Саттер рассказывает какие трудности возникают при решении этой задачи.
|
Сообщ.
#6
,
|
|
|
Возможность разносить описание шаблона и его использование по разным единицам трансляции так же предоставляет сановский компилятор (входящий в состав Sun Studio). Правда на ключевое слово "export" компилятор кладёт толстый болт, выдавая варнинг - "Export ignored -- not yet implemented.", но при этом сама такая возможность (определение шаблона - в заголовке, описание шаблона - в одном файле, использование - в другом) - реализована.
|
![]() |
Сообщ.
#7
,
|
|
Цитата antigen @ Ты, наверно, имел в виду, "объявление шаблона - в заголовке, определение шаблона - в одном файле, использование - в другом"? B.V., кажись, 8-ая фаза с её 3-им способом в действии. (определение шаблона - в заголовке, описание шаблона - в одном файле, использование - в другом) |
Сообщ.
#8
,
|
|
|
Цитата Qraizer @ определение шаблона - в одном файле, использование - в другом Да, да, именно так. И именно в такой форме, в какой с ней сталкиваются чаще всего: ![]() ![]() /* file.h:*/ template <class T> T func (T); /* end of file.h */ /* file.cpp: */ #include "file.h" template <class T> T func (T t) { return t + t; } /* end of file.cpp */ /* main.cpp: */ #include "file.h" int main () { float f; double d; func (f); // OK - Sun CC func (d); // OK - Sun CC } /* end of main.cpp */ |
![]() |
Сообщ.
#9
,
|
|
Цитата Qraizer @ B.V., кажись, 8-ая фаза с её 3-им способом в действии. Компилить на левом компиляторе только ради этой штуки -- да ну. Остановился на явных инстанцированиях. Не так изящно и универсально, но проще, чем переходить на Sun'овский или Intel'овский компиль |
![]() |
Сообщ.
#10
,
|
|
B.V., ну я не в том смысле, мол, хватай и пользуй. Я в том, что всё-таки есть такие. Хотя юы один.
|