Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Шаблоны и вложенные классы

350
22 июня 2006 года
cheburator
589 / / 01.06.2006
Содержимое collections.h:
Код:
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)    // <-- Здесь ругается
{
  ...
}

Что за ошибка такая error C2244 выходит на выделенной комментарием строке, и почему, подскажите пожалуйста.
3
22 июня 2006 года
Green
4.8K / / 20.01.2000
Для начала надо бы представлять корректный код:
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)
{
}

Только вот реализацию шаблонных классов никто не выносит в 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)
{
}
350
24 июня 2006 года
cheburator
589 / / 01.06.2006
[QUOTE=Green]Для начала надо бы представлять корректный код[/QUOTE]
Не заостряйте внимание на мелочах - я не копирую код прямо из проекта, поскольку дома инета нет, сижу на работе и ввожу весь пост руками. В проекте все правильно (насчет двух одинаковых typedef и точки запятой после классов).
В общем, спасибо за ответ, я сам выяснил в чем дело:
 
Код:
template <typename basetype>
typename basetype::CPtr< CUniCollection<basetype> > CUniCollection<basetype>::CreateCollection (CUniCollectionDescr &descr)
{
}

- я так и сделал. Просто я и раньше пытался так сделать, но компилятор ругался, что не может найти соответствующую закрывающую угловую скобку, поэтому подумал, что я пишу неправильно. Дело оказалось в том, что вот в этом месте:
 
Код:
template <typename basetype>
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)
{
  ...
};

и вызываю ее из ДРУГОГО cpp-файла без проблем и ругани со стороны линкера! Почему так...
Еще один вопрос. Как видите, функция IsEqual не зависит от параметра шаблона (тело тоже). Будет ли компилятор/линковщик генерировать разные функции IsEqual для каждого значения параметра шаблона или он "умный" и один и тот же машинный код дважды генерировать не будет?

Спасибо.
534
25 июня 2006 года
HarryAxe
448 / / 19.01.2006
[QUOTE=cheburator]Как видите, функция IsEqual не зависит от параметра шаблона (тело тоже). Будет ли компилятор/линковщик генерировать разные функции IsEqual для каждого значения параметра шаблона или он "умный" и один и тот же машинный код дважды генерировать не будет?

Спасибо.[/QUOTE] Именно потому, что он (компилятор) умный, он и позволил тебе реализовать данную функцию в отдельно компилируемом модуле (то бишь cpp файле). Если вникнуть в методы реализации шаблонов, то можно сказать, что это, по сути дела, макросы поверх классов - и не более того. Тебе же не приходит в голову компилировать макрос? Кое-что на эту тему уже рассматривалось здесь
3
25 июня 2006 года
Green
4.8K / / 20.01.2000
[QUOTE=cheburator]Не заостряйте внимание на мелочах - я не копирую код прямо из проекта, поскольку дома инета нет, сижу на работе и ввожу весь пост руками. В проекте все правильно (насчет двух одинаковых typedef и точки запятой после классов).
[/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]
Будет генерировать для каждого. Возможно есть оптимизаторы, которые это учитывают, но уже при линковке.
350
27 июня 2006 года
cheburator
589 / / 01.06.2006
[QUOTE=Green]
А вот это врядли. Ищи ответ в своем коде.
Ты, видимо, вызываешь её не только в другом срр-файле, но и в том, где её определял. Одним словом, чудес не бывает, шаблон где-то был проинстанирован уже конкретным параметром.[/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-шник
};

[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 ни о чем подобном не говорит.
3
27 июня 2006 года
Green
4.8K / / 20.01.2000
[QUOTE=cheburator]Это исключено, я в одном и том же cpp вызываю и ту, и другую функцию, (причем с одним набором параметров!), а точнее, так:
[/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
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог