class CDBTypes
{
template <typename basetype> class CPtr
{
...
}
...
}
class CMemTypes
{
template <typename basetype> class CPtr
{
...
}
...
}
template <typename> class CUniCollection
{
static typename basetype::CPtr<CUniCollection> CreateCollection(CUniCollectionDescr &descr);
...
}
typedef CUniCollection<CDBTypes> CDBUniCollection;
typedef CUniCollection<CMemTypes> CDBUniCollection;
Шаблоны и вложенные классы
Код:
Файл collections.cpp:
Код:
#include "collections.h"
template <typename basetype> typename basetype::CPtr<CUniCollection> CUniCollection<basetype>::CreateCollection (CUniCollectionDescr &descr) // <-- Здесь ругается
{
...
}
template <typename basetype> typename basetype::CPtr<CUniCollection> CUniCollection<basetype>::CreateCollection (CUniCollectionDescr &descr) // <-- Здесь ругается
{
...
}
Что за ошибка такая error C2244 выходит на выделенной комментарием строке, и почему, подскажите пожалуйста.
1) после описания классов ставится точка с запятой,
2) template <typename> class CUniCollection наверное должно было быть
template <typename basetype> class CUniCollection,
3) Определяешь типы с одинаковыми именами?
typedef CUniCollection<CDBTypes> CDBUniCollection;
typedef CUniCollection<CMemTypes> CDBUniCollection;
Далее... ты сам себя запутал. Наверное, ты хотел сделать следующее:
Код:
template <typename basetype>
typename basetype::CPtr< CUniCollection<basetype> > CUniCollection<basetype>::CreateCollection (CUniCollectionDescr &descr)
{
}
typename basetype::CPtr< CUniCollection<basetype> > CUniCollection<basetype>::CreateCollection (CUniCollectionDescr &descr)
{
}
Только вот реализацию шаблонных классов никто не выносит в cpp.
А для того, что бы не путаться делают внутренние typedef-ы:
Код:
template <typename basetype> class CUniCollection
{
typedef typename basetype::CPtr<CUniCollection> TypePtr;
static TypePtr CreateCollection(CUniCollectionDescr &descr);
};
template <typename basetype>
typename CUniCollection<basetype>::TypePtr CUniCollection<basetype>::CreateCollection (CUniCollectionDescr &descr)
{
}
{
typedef typename basetype::CPtr<CUniCollection> TypePtr;
static TypePtr CreateCollection(CUniCollectionDescr &descr);
};
template <typename basetype>
typename CUniCollection<basetype>::TypePtr CUniCollection<basetype>::CreateCollection (CUniCollectionDescr &descr)
{
}
Не заостряйте внимание на мелочах - я не копирую код прямо из проекта, поскольку дома инета нет, сижу на работе и ввожу весь пост руками. В проекте все правильно (насчет двух одинаковых typedef и точки запятой после классов).
В общем, спасибо за ответ, я сам выяснил в чем дело:
Код:
template <typename basetype>
typename basetype::CPtr< CUniCollection<basetype> > CUniCollection<basetype>::CreateCollection (CUniCollectionDescr &descr)
{
}
typename basetype::CPtr< CUniCollection<basetype> > CUniCollection<basetype>::CreateCollection (CUniCollectionDescr &descr)
{
}
- я так и сделал. Просто я и раньше пытался так сделать, но компилятор ругался, что не может найти соответствующую закрывающую угловую скобку, поэтому подумал, что я пишу неправильно. Дело оказалось в том, что вот в этом месте:
Код:
template <typename basetype>
typename basetype::CPtr< CUniCollection<basetype>/*вот здесь*/ > CUniCollection<basetype>::CreateCollection (CUniCollectionDescr &descr)
typename basetype::CPtr< CUniCollection<basetype>/*вот здесь*/ > CUniCollection<basetype>::CreateCollection (CUniCollectionDescr &descr)
нужен пробел :) иначе компилятор видит operator >>.
[QUOTE=Green]
Только вот реализацию шаблонных классов никто не выносит в cpp.[/QUOTE]
Зачем? Если вышеуказанная функция CreateCollection огромна, и будет описана непосредственно в header-файле, она же будет включаться во всякий .cpp-файл, где он #include-ится.
-------
В общем, теперь вопрос другой. Я вызываю эту самую функцию CreateCollection из другого .cpp-файла. Компилятор относится к этому нормально, но линкер кричит, что эта функция нигде не реализована, хотя я ее тело реализовал в collections.cpp.
Это бы еще ладно. Но фишка в том, что я описал в вышеуказанном классе другую функцию
Код:
collections.h:
template <typename basetype> class CUniCollection
{
...
BOOL IsEqual (void *data1, void *data2, LPCSTR name);
...
};
collections.cpp:
template <typename basetype> BOOL CUniCollection<basetype>::IsEqual (void *data1, void *data2, LPCSTR name)
{
...
};
template <typename basetype> class CUniCollection
{
...
BOOL IsEqual (void *data1, void *data2, LPCSTR name);
...
};
collections.cpp:
template <typename basetype> BOOL CUniCollection<basetype>::IsEqual (void *data1, void *data2, LPCSTR name)
{
...
};
и вызываю ее из ДРУГОГО cpp-файла без проблем и ругани со стороны линкера! Почему так...
Еще один вопрос. Как видите, функция IsEqual не зависит от параметра шаблона (тело тоже). Будет ли компилятор/линковщик генерировать разные функции IsEqual для каждого значения параметра шаблона или он "умный" и один и тот же машинный код дважды генерировать не будет?
Спасибо.
Спасибо.[/QUOTE] Именно потому, что он (компилятор) умный, он и позволил тебе реализовать данную функцию в отдельно компилируемом модуле (то бишь cpp файле). Если вникнуть в методы реализации шаблонов, то можно сказать, что это, по сути дела, макросы поверх классов - и не более того. Тебе же не приходит в голову компилировать макрос? Кое-что на эту тему уже рассматривалось здесь
[/QUOTE]
Это не мелочи. Для того, чтобы разобраться, что не так в твоем навороченном коде, мне для начало надо было его скомпилировать. А править при том с десяток "мелочей" весьма неблагодарное занятие. Уважайте время других!
[QUOTE=cheburator]
Зачем? Если вышеуказанная функция CreateCollection огромна, и будет описана непосредственно в header-файле, она же будет включаться во всякий .cpp-файл, где он #include-ится.
[/QUOTE]
За тем, что бы не задавать следующего своего вопроса:
[QUOTE=cheburator]
В общем, теперь вопрос другой. Я вызываю эту самую функцию CreateCollection из другого .cpp-файла. Компилятор относится к этому нормально, но линкер кричит, что эта функция нигде не реализована, хотя я ее тело реализовал в collections.cpp.
[/QUOTE]
Это неоднократно обсуждалось в форуме.
Кроме того, открою страшную тайну. Ничего плохого в том, что "огромная функция будет включаться во всякий .cpp-файл" нет ничего страшного, т.к. компилироваться она будет лишь там, где будет использоваться, в остальных местах она будет просто игнорирована.
[QUOTE=cheburator]
Это бы еще ладно. Но фишка в том, что я описал в вышеуказанном классе другую функцию
и вызываю ее из ДРУГОГО cpp-файла без проблем и ругани со стороны линкера! Почему так...
[/QUOTE]
А вот это врядли. Ищи ответ в своем коде.
Ты, видимо, вызываешь её не только в другом срр-файле, но и в том, где её определял. Одним словом, чудес не бывает, шаблон где-то был проинстанирован уже конкретным параметром.
[QUOTE=cheburator]
Еще один вопрос. Как видите, функция IsEqual не зависит от параметра шаблона (тело тоже). Будет ли компилятор/линковщик генерировать разные функции IsEqual для каждого значения параметра шаблона или он "умный" и один и тот же машинный код дважды генерировать не будет?
Спасибо.[/QUOTE]
Будет генерировать для каждого. Возможно есть оптимизаторы, которые это учитывают, но уже при линковке.
А вот это врядли. Ищи ответ в своем коде.
Ты, видимо, вызываешь её не только в другом срр-файле, но и в том, где её определял. Одним словом, чудес не бывает, шаблон где-то был проинстанирован уже конкретным параметром.[/QUOTE]
Это исключено, я в одном и том же cpp вызываю и ту, и другую функцию, (причем с одним набором параметров!), а точнее, так:
Код:
typedef CUniCollection<CDBType> CDBUniCollection;
...
BOOL CApp:InitInstance ()
{
...
CPtr<CDBUniCollection> coll = CDBUniCollection::CreateCollection (descr); // Здесь линкер ругается
c1 = new CDBUniCollection;
c1->IsEqual (0, 0, 0); // А это катит, и если убрать обращение к CreateCollection, получается "рабочий" exe-шник
};
...
BOOL CApp:InitInstance ()
{
...
CPtr<CDBUniCollection> coll = CDBUniCollection::CreateCollection (descr); // Здесь линкер ругается
c1 = new CDBUniCollection;
c1->IsEqual (0, 0, 0); // А это катит, и если убрать обращение к CreateCollection, получается "рабочий" exe-шник
};
[QUOTE=Green]
Будет генерировать для каждого. Возможно есть оптимизаторы, которые это учитывают, но уже при линковке.[/QUOTE]
HarryAxe прав - компилятор (по крайней мере, MS VC 2003) достаточно умен, чтобы сразу сгенерировать объектный код для функции, не зависящей от параметров шаблона. Поэтому линкер не ругается. До этого я уже догадался. Подтверждение тому - можете ручками открыть obj файл и посмотреть.
[QUOTE=Green]
Кроме того, открою страшную тайну. Ничего плохого в том, что "огромная функция будет включаться во всякий .cpp-файл" нет ничего страшного, т.к. компилироваться она будет лишь там, где будет использоваться, в остальных местах она будет просто игнорирована.
[/QUOTE]
Ничего страшного с точки зрения получающегося кода (т. е. повторного кода, мы, естественно, не получим, это понятно).
Страшно то, что 1) нарушается хороший и выработанный годами принцип модульного программирования; 2) если файл с шаблонами большой и #include-ится в много файлов, время компиляции может значительно вырасти. Но главное 3) в каком-то смысле нарушается инкапсуляция - пользователь моих шаблонов получает возможность открыть и нагло посмотреть мой код. А так я мог бы, скажем, дать ему только header и obj. Или вовсе dll, lib и header.
В общем, было бы хорошо, если бы была директива или что-то еще, позволяющая "насильно" заставить генерировать код для конкретной реализации шаблона. Тогда б я в collections.cpp добавил две директивы для CUniCollection<CDBType> и CUniCollection<CMemType>. Типа,
#pragma forceimplement (CUniCollection<CDBType>);
К сожалению, MSDN ни о чем подобном не говорит.
[/QUOTE]
ЧУдес не бывает! Шаблон должен быть инстанирован.
Значит инстанирование происходит в том cpp, где реализована эта функция.
Ты представляешь отдельные фрагменты, по которым невозможно судить о сборке в целом. Для представления картины сборки надо иметь ВСЕ исходники и в том виде, в котором ты их собираешь, а не отдельные выдержки их некоторых файлов.
[QUOTE=cheburator]
HarryAxe прав - компилятор (по крайней мере, MS VC 2003) достаточно умен, чтобы сразу сгенерировать объектный код для функции, не зависящей от параметров шаблона. Поэтому линкер не ругается. До этого я уже догадался. Подтверждение тому - можете ручками открыть obj файл и посмотреть.
[/QUOTE]
О... метод научного тыка в действии.
Изучение документации приносит больше пользы и меньше домыслов.
Попробуй заменить опцию оптимизатора линковки с Eliminate Unreferenced Data (/OPT:REF) на Keep Unreferenced Data (/OPT:NOREF)
[QUOTE=cheburator]
Страшно то, что 1) нарушается хороший и выработанный годами принцип модульного программирования;
[/QUOTE]
это какой ещё?
[QUOTE=cheburator]
2) если файл с шаблонами большой и #include-ится в много файлов, время компиляции может значительно вырасти.
[/QUOTE]
А кто сказал, что шаблоны - это дешевый инструмент?
Увеличивается не только время компиляции, но и размер продукта.
[QUOTE=cheburator]
Но главное 3) в каком-то смысле нарушается инкапсуляция - пользователь моих шаблонов получает возможность открыть и нагло посмотреть мой код.
[/QUOTE]
Это по-твоему главное? :D
Это мелочи.
[QUOTE=cheburator]
В общем, было бы хорошо, если бы была директива или что-то еще, позволяющая "насильно" заставить генерировать код для конкретной реализации шаблона.
[/QUOTE]
Смотри форум: http://forum.codenet.ru/showthread.php?t=26753