[C++]Как правильно использ. уже определенный в другом файле шаблон?

zrab


// file1.cpp
#include file1.h
template<class T> class A
{
// дальше что-то определяю в классе А
}


// file1.h
template<class T> class A;


// main.cpp
#include file1.h
int main(array<System::String ^> ^args)
{
// пытаюсь использовать класс А
}

Так вот: в файле main.cpp я попытался вызвать конструктор класса А (public, не private а компилятор ругается. В литературе написано, что в файле file1.h надо писать не
 template<class T> class A 


export template<class T> class A 

но компилятор опять ругается. В чем причина?

Что самое главное - с шаблонами функций все получалось нормально.

pitrik2

ты должен описание шаблона в заголовок (.h) класть
а в .cpp только реализацию
шаблоны тут ни при чем
ты сначала добейся работы без шаблонов, просто с классом
// A.cpp
void A::f
{
// реализация функции 1
}
void A::g
{
// реализация функции 2
}
// A.h
// описание класса
class A
{
public void f;
public void g;
}
// main.cpp
include "A.h"
new A; // использование

zrab

Спасибо, мысль понятна. Но ты в файле A.cpp определяешь ф-и, которые используешь в A.h. Почему же нельзя так делать с классами (или шаблонами классов)?

mkrec

Потому что работать с классом ты можешь вне зависимости от того, имеешь ли ты исходный код его реализации. А чтобы из шаблонов сгенерить классы, ты должен знать исходник. И чтобы пользоваться шаблонами, твой main.cpp должен знать, что же из них реально получилось. Что-то в этом духе.

freezer

и в случае Managed C++ то же самое? :confused:

erotic

Есть мнение, что в заголовки стоит писать не C++, а Managed C++, а еще лучше C++/CLI. Потому что корявым дополнениям от MS никто не давал права занимать уже существующие названия и вводить пользователей в заблуждение.
Потому что если бы в теме было сразу ясно указано, что за язык, я бы даже не полез в нее, а так я уже собрался было ответить, а тут облом.

Dasar

и в случае Managed C++ то же самое?
конечно, MC++ имеют все ограничения обычных плюсов

Dasar

Есть мнение, что в заголовки стоит писать не C++, а Managed C++, а еще лучше C++/CLI.
данный вопрос ни какого отношения к mc++ не имеет.
вопрос касается обычных проблем плюсов.
ps
MC++ кстати очень хорошо сделан, в духе оригинальных C++.

erotic

данный вопрос ни какого отношения к mc++ не имеет.вопрос касается обычных проблем плюсов.
Вполне возможно, но, поскольку в MC++ я абсолютно некомпетентен, а код там приведен именно для него, то и ответить мне нечего, и читать оказалось не интересно.

Dasar

> Вполне возможно, но, поскольку в MC++ я абсолютно некомпетентен, а код там приведен именно для него
в какой это строчке кода?
этот код должен собираться в любом компиляторе C++ с такими же проблемами.

Dasar

> в какой это строчке кода?
int main(array<System::String ^> ^args)
сначала не заметил крышек, но этот код по синтаксису полностью эквивалентен
int main(array<System::String *> *args)
крышка только обозначает, что указатель обрабатывается GC

erotic

сначала не заметил крышек, но этот код по синтаксису полностью эквивалентенint main(array<System::String *> *args)
Ага, а что такое в С++ array?
Ну, разве что по виду это похоже на какой-то шаблон, но вообще это ключевое слово, по-моему, о котором я также ничего не знаю.

freezer

то есть, в MC++ дженерики не поддерживаются?

Dasar

Ага, а что такое в С++ array?
очевидно, что что-то такое:
#include "array.h"
ps
по смыслу обычный шаблонный класс

tamusyav

A.h вообще ничего не использует. Это просто кусок кода, который присоединяется к .cpp-файлам. А вот уже после этого присоединения и начинается выяснение того, кто что использует и какую информацию он должен для этого знать. Здесь важно понимать, где граница между действиями компилятора, который обрабатывает .cpp-файлы по отдельности (в соответствии с инструкциями препроцессора включая в них код из .h-файлов) и компоновщика (линкера который, не зная ничего про .cpp и .h-файлы, собирает конечный код из результатов работы компилятора и библиотек. Компилятору, когда он встречает вызов функции или обращение к переменной, достаточно наличия объявления, то есть ему не нужно знать исходный код функции или где именно находится переменная (и какое у нее начальное значение); он лишь вставляет в место обращения сигнатуру и генерирует код для передачи/получения параметров. Таким образом, объявление функции не является ее использованием - чтобы указать, что функция принимает и выдает значения таких-то типов, знать ее код не требуется. Точно так же для оформления ее вызова компилятору достаточно знания той информации, которая есть в объявлении.
Компоновщик, который вызывается после компиляции всех файлов с исходным кодом, просматривает результаты компиляции и, как из кирпичей, собирает программу, вставляя на место сигнатур соответствующие вызовы.
Следует обратить внимание на то, что компоновщик ничего не знает ни о классах, ни о шаблонах. С ними работает исключительно компилятор. Поэтому если в некотором месте кода используется хоть какая-то информация о классе X (кроме того, что идентификатор X обозначает некоторый класс то необходимо, чтобы к этому моменту было известно определение класса. Если достаточно лишь информации о том, что X является некоторым классом, то можно ограничиться объявлением:
class X;
С шаблонами еще интереснее. Компилятор может сгенерировать код для каждой функции из шаблонного класса только при условии того, что ему известны все входящие в нее типы (и прочие параметры шаблона, если такие есть). Если описание этих функций включено в .h-файл, то никаких проблем нет - компилятору известно, с какими параметрами будет использоваться шаблон, и для каждого использованного набора этих параметров он сгенерирует необходимый код (процедура генерации кода для заданного набора параметров шаблона называется инстанцированием). Если описание функций включено в отдельный файл x.cpp, а шаблон класса - в файл x.h, то компилятор ошибок выдавать не будет как при компиляции файла x.cpp, так и при компиляции некоторого файла a.cpp, использующего этот шаблон, - все необходимые объявления присутствуют. Ошибки будут на стадии компоновки. Дело в том, что при компиляции x.cpp компилятор не знает, с какими наборами параметров будет использоваться шаблон (так как он используется только в файле a.cpp). И поскольку в файле x.cpp этот шаблон не используется ни с каким набором параметров, то компилятор вообще никакого кода не сгенерирует! А при компиляции файла a.cpp компилятор не имеет исходного кода вызываемых функций, поэтому также их не скомпилирует, но вызовы правильно оформит, так как объявления этих функций ему доступны; то есть он сможет инстанцировать класс, но не сможет инстацировать функции-члены этого класса.
Если громоздкий код функций не хочется вставлять в .h-файл, то есть другое решение, называемое явным инстанцированием шаблона. Суть его заключается в том, что в .cpp-файл со всеми описаниями функций вставляется инструкция наподобие следующей
template class X<double>;
которая указывает компилятору, что описанные функции необходимо инстанцировать для типа double. Этих инструкций может быть несколько (с различными наборами параметров тогда инстанцирование будет выполнено для каждого указанного набора.
Некоторые детали я здесь опустил (и все равно простыня получилась :) но вроде ничего существенного не забыл.
Оставить комментарий
Имя или ник:
Комментарий: