RXL
Технический
Администратор
Offline
Пол:
|
|
« : 17-12-2010 11:47 » |
|
Пытаюсь въехать в шаблоны и немного недопонимаю... #include <map>
using namespace std;
template <class A, class B, class C> class X { // ... };
template <class A, class B, class C> class Y { public: typedef X<A, B, C> X; typedef map<int, X*> XCollection;
}; Ошибка в отмеченной строке. Штудирую Страуструпа и не могу найти ответ. Ну, или могз перегрузился. Объясните, пожалуйста, мою ошибку. Добавлено через 2 минуты и 17 секунд:Ёпрст... Все фишка была только в том, что я не подгрузил map (в приведенном примере он есть, а у меня - нет). Однако, сообщения gcc не интуитивно понятны. Добавлено через 3 минуты и 35 секунд:Тогда другой вопрос: в выше приведенном примере есть X<A,B,C> и Y::X. Это идентичные типы или разные? Не будет ли у меня проблем с этим? Например: class Xaaa : public X<int, int, int> { // ... };
class Yaaa : public Y<int, int, int> { // ... }; Здесь Xaaa и Yaaa::X идентичны или это разные классы?
|
|
« Последнее редактирование: 17-12-2010 11:54 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #1 : 17-12-2010 12:21 » |
|
есть X<A,B,C> и Y::X. Это идентичные типы или разные? По-моему typedef - это аналог макроподстановки (не препроцессорный), поэтому название нового типа полностью эквивалентно определению. Сами X и X, конечно, разные. При этом внутри Y больший приоритет имеет ближайшее определение X в typedef, а не внешнее определение. Однако при попытке описания методов вне класса это уже будет не так, и Y::X нужно будет указывать явно. Здесь Xaaa и Yaaa::X идентичны или это разные классы? Здесь классы, конечно, разные, поскольку наследование - это не переопределение через typedef. Ты можешь в оба эти класса добавить собственное разное содержание - тогда их различие станет более очевидным для тебя.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
npak
|
|
« Ответ #2 : 17-12-2010 13:26 » |
|
RXL,
во втором примере Yaaa::X - это синоним для class X<int,int,int>, так как он унаследован из базового класса class Y<int,int,int>.
|
|
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #3 : 17-12-2010 13:44 » |
|
Еще вопрос. #include <map>
using namespace std;
template <class A, class B, class C> class Y { public: typedef map<int, A*> ACollection; typedef ACollection::const_iterator ACIterator; // или напрямую использовать ACollection::const_iterator }; В отмеченной строке имею ошибку. type ‘std::map<unsigned int, A*, std::less<unsigned int>, std::allocator<std::pair<const unsigned int, A*> > >’ is not derived from type ‘Y<A, B, C>’
Я понимаю только, что не понимаю шаблонов полностью. В простых случаях трудности не возникают, а тут - даже не знаю, в какую сторону думать.
|
|
« Последнее редактирование: 17-12-2010 13:47 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #4 : 17-12-2010 14:25 » |
|
RXL, по-моему надо так: typedef typename ACollection::const_iterator ACIterator; Поскольку ACollection - это синоним некоторого определения, построенного с использованием параметра шаблона, параметры шаблона не обязательно бывают типами, а компилятор не умеет при разворачивании дерева подстановок контролировать, что типом является, а что не является, нужно ему помогать при помощи ключевого слова typename - явно указывая, что следующий за этим словом идентификатор (или конструкция) является типом. Подробнее не скажу - я в этой части стандарт в деталях не изучал. Но в общем и целом это немного похоже на проблемы с макросами для препроцессора.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
npak
|
|
« Ответ #5 : 17-12-2010 15:15 » |
|
RXL, это называется dependent name - зависимое имя.
Фишка в том, что в С++ разрешается делать специализации шаблонов - для определенного сочетания параметров шаблонов ты можешь создать отдельный тип, который будет совсем не похож на типы, инстанциированные из общего шаблона. По этой причине С++ требует, чтобы имена типов, объявленных в шаблонах, в других шаблонах использовались с префиксом typename
Тем самым ты подсказываешь компилятору, что ты декларируешь именно тип
|
|
« Последнее редактирование: 17-12-2010 15:20 от npak »
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #6 : 18-12-2010 10:10 » |
|
Dimka, typename ставил, но это вызывает другую ошибку. У Страуструпа сказано, что typename нужно указывать в случаях неоднозначности интерпретации.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
npak
|
|
« Ответ #7 : 18-12-2010 10:17 » |
|
Dimka, typename ставил, но это вызывает другую ошибку.
Да ладно - я прогнал твой пример с typename в gcc 4.3 #include <map>
using namespace std;
template <class A, class B, class C> class Y { public: typedef map<int, A*> ACollection; typedef typename ACollection::const_iterator ACIterator; // Collection::const_iterator }; Компилируется без возражений
|
|
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #8 : 18-12-2010 10:48 » |
|
Заголовок скомпилировался. Набор каких-то странных ошибок. Сейчас напишу тест и посмотрим, работает ли это.
У меня gcc 4.1.2
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
npak
|
|
« Ответ #9 : 18-12-2010 10:49 » |
|
Рома, у тебя какой компилятор? Поддержка шаблонов С++ в gcc моложе 4.3 не полностью соответствует стандарту.
|
|
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #10 : 18-12-2010 13:05 » |
|
#include <map> using namespace std; /* template X */ template<class A, class B> class X { protected: A *a; B *b; public: X(A *a, B *b) : a(a), b(b) { } ~X() { } }; /* template Y */ template<class X, class A, class B> class Y { public: typedef map<int, X*> XCollection; typedef XCollection::iterator Iterator; protected: A *a; B *b; XCollection x; public: Y(A *a, B *b) : a(a), b(b) { } ~Y() { } Iterator begin() { return x.begin(); } Iterator end() { return x.end(); } }; /* ********** */ class TA { }; class TB { }; typedef X<TA, TB> _TX; class TX : public _TX { public: TX(TA *a, TB *b) : _TX(a, b) { } }; typedef Y<TX, TA, TB> _TY; class TY : public _TY { public: TY(TA *a, TB *b) : _TY(a, b) { } }; /* ********** */ int main() { TY y(); return 0; }
$ g++ -Wall -c tpl_class_test1.cpp tpl_class_test1.cpp:24: error: type ‘std::map<int, X*, std::less<int>, std::allocator<std::pair<const int, X*> > >’ is not derived from type ‘Y<X, A, B>’ tpl_class_test1.cpp:24: error: expected ‘;’ before ‘Iterator’ tpl_class_test1.cpp:35: error: ‘Iterator’ does not name a type tpl_class_test1.cpp:36: error: ‘Iterator’ does not name a type
Рома, у тебя какой компилятор? Поддержка шаблонов С++ в gcc моложе 4.3 не полностью соответствует стандарту.
Старенький - 4.1.2...
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #11 : 18-12-2010 16:23 » |
|
Ну я на GCC 4.2.1 (20070719) проверял...
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #12 : 18-12-2010 16:58 » |
|
Пробую на виртуалке с FC12. # cat /etc/issue Fedora release 12 (Constantine) # g++ --version g++ (GCC) 4.4.3 20100127 (Red Hat 4.4.3-4) Copyright (C) 2010 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # g++ -Wall -c tpl_class_test1.cpp tpl_class_test1.cpp:24: error: type ‘std::map<int, X*, std::less<int>, std::allocator<std::pair<const int, X*> > >’ is not derived from type ‘Y<X, A, B>’ tpl_class_test1.cpp:24: error: expected ‘;’ before ‘Iterator’ tpl_class_test1.cpp:35: error: ‘Iterator’ does not name a type tpl_class_test1.cpp:36: error: ‘Iterator’ does not name a type
GCC 4.4.3. Результат тот же. Я вообще правильно пишу код (в посте №10) или опять чего не допонял? Добавлено через 1 час, 2 минуты и 58 секунд:В BCB6 компиляция прошла без проблем. Я использовал GCC для предварительного написания и тестирования классов, а потом переносил код в BCB6 и дополнял VCL-спецификой. Видимо придется сразу в BCB6 писать и тестировать.
|
|
« Последнее редактирование: 18-12-2010 18:01 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #13 : 18-12-2010 19:04 » |
|
Странные вещи:
1. Наследую от созданного по шаблону класса. Переопределяю метод m2() в своем классе. Вызываю метод m1(), определенный в шаблоне и вызывающий m2(). Отладчик показывает, что мой переопределенный метод игнорируется.
2. Указываю методы в шаблоне, которые хочу переопределить, как "virtual ... = 0". Код начинает работать как надо. Смотрю в Страуструпа и вижу, что он запрещает объявлять virtual-методы в шаблоне.
|
|
« Последнее редактирование: 18-12-2010 19:19 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
npak
|
|
« Ответ #14 : 18-12-2010 19:13 » |
|
typedef typename XCollection::iterator Iterator;
|
|
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #15 : 18-12-2010 19:18 » |
|
Коль, у меня без typename работает в BCB6. Его обязательно указывать? Кстати, с typename тоже работает. Компилятор старый - тоже может отклоняться от правил.
|
|
« Последнее редактирование: 18-12-2010 19:24 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #16 : 18-12-2010 19:53 » |
|
RXL, ну раз без него не работает в GCC, то обязательно. Оно же ничему не мешает - чем больше, тем лучше 1. Наследую от созданного по шаблону класса. Переопределяю метод m2() в своем классе. Вызываю метод m1(), определенный в шаблоне и вызывающий m2(). Отладчик показывает, что мой переопределенный метод игнорируется.
2. Указываю методы в шаблоне, которые хочу переопределить, как "virtual ... = 0". Код начинает работать как надо. Смотрю в Страуструпа и вижу, что он запрещает объявлять virtual-методы в шаблоне.
А вот это непонятно. Так что ли? template<class T> class X { protected: virtual void m2() = 0; public: void m1() { this->m2(); } };
class Y: public X<int> { protected: void m2() { cout << "m2" << endl; } }; И как тут обойтись без виртуальных методов? Там Страуструп заодно не советует отказаться от сочетания наследования и шаблонов?
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #17 : 18-12-2010 20:00 » |
|
Да, Дим, пример подходящий.
Не, Старуструп в норме - я опять лажанулся: он запрещает виртуализировать член-шаблон. Постараюсь быть внимательнее, но при неожиданно свалившемся объеме информации это бывает затруднительно.
Мне нравится эта книга, но т.к. я читаю его не всего, а использую как справочник, то порой примеров из параграфа не хватает или не понимаю.
P.S.: после скриптовых языков C++ видится довольно сложным. Вопрос привычки...
|
|
« Последнее редактирование: 18-12-2010 20:03 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #18 : 18-12-2010 20:36 » |
|
Мне нравится эта книга, но т.к. я читаю его не всего, а использую как справочник, то порой примеров из параграфа не хватает или не понимаю. А это и есть справочник Только литературно оформленный. Я его честно пытался раза 3 начинать читать как книгу - и ни разу не удалось. Самое оптимальное - читать главы по потребности (благо, перекрёстные ссылки в тексте имеются). после скриптовых языков C++ видится довольно сложным. Все эти беды от строгой типизации и родовых черт C. Зато строгая типизация даёт возможность генерировать высокопроизводительный код. Так что хотя бы для более глубокого понимания программирования, по-моему, полезно познакомиться.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #19 : 19-12-2010 16:54 » |
|
Добил свой проектик - на BCB6 собирается на ура. Правда, у ихнего компилятора тоже не мало заскоков.
Оцениваю сокращение кода за счет выноса общих частей модулей (их у меня 9 штук по три класса) в шаблоны примерно в 10 раз, трудоемкость написания вышла примерно такая же или немного больше (без шаблонов был бы копипаст с правкой, доводкой и тестированием в каждом модуле), а вот изменять выходит много проще (только если не изменять конструкторы в шаблоне).
Подмечаю такую особенность, что если метод шаблонного класса в программе не используется, то компилятор выкидывает его из фактического класса.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
npak
|
|
« Ответ #20 : 19-12-2010 18:39 » |
|
Подмечаю такую особенность, что если метод шаблонного класса в программе не используется, то компилятор выкидывает его из фактического класса.
Стандарт явным образом запрещает (should not) инстанцировать сущности (методы, статические данные), которые не используются в программе, за исключением явного инстанцирования.
|
|
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #21 : 19-12-2010 19:28 » |
|
Подмечаю такую особенность, что если метод шаблонного класса в программе не используется, то компилятор выкидывает его из фактического класса. А как ты это подметил? Я тут в связи с этим задумался о раздельной компиляции и всяких финтах ушами с наследованием инстанцированных шаблонных классов...
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
npak
|
|
« Ответ #22 : 19-12-2010 19:35 » |
|
Я тут в связи с этим задумался о раздельной компиляции и всяких финтах ушами с наследованием инстанцированных шаблонных классов...
раздельная компиляция шаблонов - это поиск приключений на свою ... голову, скажем так.
|
|
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #23 : 19-12-2010 20:15 » |
|
Dimka, в процессе тестирования: в некоторых случаях ошибки компиляции возникали только если использовать метод, в котором была ошибка.
А что вы имеете в виду под "раздельная компиляция шаблонов" и "наследованием инстанцированных шаблонных классов"?...
|
|
« Последнее редактирование: 19-12-2010 20:17 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
lapulya
Молодой специалист
Offline
|
|
« Ответ #24 : 19-12-2010 20:33 » |
|
npak, раздельная компиляция шаблонов - это поиск приключений на свою ... голову, скажем так. Стандартэтого делать не запрещает, просто разработчики компиляторов пока не забацали реализацию... правда когда я сморел в эту сторону последний раз на дворе был 200 год... PS Для нормальной работы (в версиях компиллеров 2002 года ))) ) реализация шаблонного класса должна быть в том же файле что и объявление, это я про раздельную компиляцию ну а наследование инстацированных шаблонных классов... я пока не понимаю, что именно ты не понимаешь))
|
|
« Последнее редактирование: 19-12-2010 20:38 от lapulya »
|
Записан
|
С уважением Lapulya
|
|
|
Антон (LogRus)
|
|
« Ответ #25 : 20-12-2010 04:11 » |
|
Dimka, в процессе тестирования: в некоторых случаях ошибки компиляции возникали только если использовать метод, в котором была ошибка.
Стандарт предписывает, для шаблонного всего, на этапе компиляции делать минимальные проверки и только того, что не зависит от параметров шаблона, естетсвенно до тех пор пока не будет инстацирования, тогда проверки, как для обычных функций VC++ при этом пошла дальше, она вообще не проверять содержимое шаблона, если он не используется. Как-то так. Разделением реализации и обявления шаблона, я развлекался, нормальная практика ничего тут срашного нет, только приходится явно истанцировать шаблоны по мере необходимости, а то линковщик ругается, что нет реализации методов. На старой работе у нас было куча кода с явным инстанцированием и раздельной реализией, так софтина собиралась быстрее и не падал компилятор. Шаблоны были очень большие и когда мы оставляли их в хедерах, то: 1. Инстанцирование происходило в каждой единице трансляции, а это отчень не быстро 2. компилятор запросто заваливался с ошибокой - алярм, достигнуто максимальное количество элементов на единицу трансляции (развернёт все методы, переменные и т.д.) - эта ошибка характерна для MS VC++ в gcc её небыло
|
|
|
Записан
|
Странно всё это....
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #26 : 20-12-2010 04:48 » |
|
Антон, можешь дать пример? Я пока не врубаюсь, как можно разделить шаблон и потом применить его.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #28 : 20-12-2010 07:49 » |
|
Столько комментариев, и всё не о том, что я имел в виду Я имел в виду не "раздельную компиляцию шаблонов", а сам факт раздельной компиляции файлов проекта. Т.е. то, что в разных объектных файлах в контексте одного класса могут присутствовать ссылки на разные подмножества вызываемых методов. Вот мне и непонятно: выкидывает неиспользуемые методы линкер? Ведь компилятор работает раздельно для каждого объектного файла и заранее не может узнать, какие методы понадобятся, а какие нет.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
npak
|
|
« Ответ #29 : 20-12-2010 08:10 » |
|
так же как с обычным разделением на объявление и реализацию templ.cpp #include <iostream> #include "templ.h"
template <typename T> void TSome<T>::print() { std::cout << "TSome::print()\n"; }
template class TSome<int>; template class TSome<char>; Ага, вот об этом я и говорил. В результате получилось, что у тебя определены только классы TSome<int> и TSome<char>. Если добавить в main TSome<short>, то ни gcc 4.3, ни MS VC 10 не смогут слинковать. Скажут, что отсутствует символ TSome<short>::print(void) Стандарт С++ оставляет поддержку такой схемы на откуп разработчикам. Вот как описывается инстанциирование шаблонов в стандарте (2.1 Phases of compilation, paragraph 8 ) Each translated translation unit is examined to produce a list of required instantiations. [Note: this may include instantiations which have been explicitly requested (14.7.2). ] The definitions of the required templates are located. It is implementation-defined whether the source of the translation units containing these definitions is required to be available.
|
|
« Последнее редактирование: 20-12-2010 08:12 от npak »
|
Записан
|
|
|
|
|