Версия для печати темы
Форум на CrossPlatform.RU _ С\С++ _ Упреждающее объявление (forward declaration) для шаблонных классов
Автор: daorus 30.10.2010, 20:18
Какой синтаксис форвад-декларации для типа хеш, если я хочу в классе хранить на него указатель
// my.h
class QHash; // не хватает
class QString; // хватает
class my
{
public:
my();
QHash<QString, QString>* dic;
};
// my.cpp
#include <QHash>
my::my()
{
dic = new QHash<QString, QString>;
}
Автор: Алексей1153 30.10.2010, 20:25
помести
#include <QHash>
#include <QString>
в заголовочный файл
ну а если поизвращаться всё равно надо, то сделай класс-оболочку, а вот его уже форвардни
Автор: Litkevich Yuriy 30.10.2010, 20:30
Здесь для QString этот номер не пройдёт, т.к. используются не указатели на него.
Вообще forward declaration возможен только в случае применения указателей. Т.к. их размер (в памяти) постоянен
Автор: kwisp 30.10.2010, 20:33
интересный вопрос. что на данный вопрос отвечает гугл?
просто так пальцем в небо.
template <>
class QHash<QString, QString>;
не схавает?
а вообще конечно - такого рода штуки могут быть непереносимы так что, можно и отказаться от такого рода forward declaration
Цитата(Litkevich Yuriy @ 30.10.2010, 21:30)

в случае применения указателей.
а ссылок?
Автор: Litkevich Yuriy 30.10.2010, 20:46
Цитата(kwisp @ 30.10.2010, 23:33)

а ссылок?
тут не уверен, возможно, тоже пройдёт
Автор: Алексей1153 30.10.2010, 20:51
ссылка - также, как и указатель. Потому что это одно и тоже для компилятора
Автор: igor_bogomolov 30.10.2010, 21:25
Цитата(daorus @ 30.10.2010, 21:18)

Какой синтаксис форвад-декларации для типа хеш, если я хочу в классе хранить на него указатель
Да все просто на самом деле
class QString;
template <class Key, class T>
class QHash;
class my
{
public:
my();
QHash<QString, QString>* dic;
};
// my.cpp
#include <QHash>
my::my()
{
dic = new QHash<QString, QString>;
}
Автор: Алексей1153 30.10.2010, 21:52
Игорь, сильно сомневаюсь
template <class Key, class T>
class QHash;
QHash<QString, QString>* dic;
а ты проверил, компилится ?
Будет ведь переопределение класса QHash из его инклуда
Автор: igor_bogomolov 30.10.2010, 22:07
Цитата(Алексей1153 @ 30.10.2010, 22:52)

Игорь, сильно сомневаюсь
На всякий случай проверил, все работает. Хотя и так знал. В исходниках Qt довольно часто используется такое предварительное объявление шаблонных классов.
Цитата(Алексей1153 @ 30.10.2010, 22:52)

Будет ведь переопределение класса QHash из его инклуда
Вообще этой фразы не понял. Какое еще переопределение?
В .h файле нам всего лишь нужно указать тип класса. Никаких уточнений не нужно, так как создается указатель и инстанцирования не происходит. В момент инстанцирования вся информация о параметрах шаблона уже есть
Автор: Алексей1153 30.10.2010, 22:15
igor_bogomolov, всё, дошло до меня
Я просто никогда так не предопределял шаблоны
Автор: daorus 31.10.2010, 0:00
Цитата(igor_bogomolov @ 31.10.2010, 0:25)

Да все просто на самом деле
Круто. Огромное спасибо!
====
Раз уж тут чисто по синтаксису вопрос задал. Есть один вопрос, не знаю где спросить. Вот грядет стандарт С+. Там есть лямбды, т.е. функции без сигнатур. А они вообще в кути могут хоть какую-то полезную нагрузку нести или эти лямбды - это просто "с боку бантик"? Сорри за оффтопик, есличо.
Автор: Litkevich Yuriy 31.10.2010, 0:45
daorus, лучше следовать правилу:
Одна проблема (вопрос) - одна тема. Так потом проще искать, если приспичит
Автор: Iron Bug 31.10.2010, 11:08
Цитата(daorus @ 30.10.2010, 22:18)

Какой синтаксис форвад-декларации для типа хеш, если я хочу в классе хранить на него указатель
а где в приведённом коде форвард-декларация-то? форвард-декларацией называется случай, когда класс ссылается сам на себя или на потомков. ну и всё сказанное (насчёт указателей) тогда справедливо. но в примере никакой форвард-декларации нет. обычное использование шаблонного класса. стандартного инклюда должно хватить. главное, если компилить под вендой, с MSVC, то нельзя выносить реализацию шаблонного класса из его объявления (это баг MSVC, несовместимость со стандартами). то есть, в заголовочнике должен быть весь класс и заголовочник должен быть указан до использования где-либо в коде.
Автор: Litkevich Yuriy 31.10.2010, 11:30
Цитата(Iron Bug @ 31.10.2010, 14:08)

когда класс ссылается сам на себя или на потомков
не только. Любой другой класс тоже может быть использован.
Автор: Алексей1153 31.10.2010, 11:34
Цитата(Iron Bug @ 31.10.2010, 14:08)

то есть, в заголовочнике должен быть весь класс и заголовочник должен быть указан до использования где-либо в коде.
о да. Столкнулся несколько раз, пытался решить, спрашивал советы - так и оставил в заголовочнике весь шаблон.
Но душа то не удовлетворена ))) Изворачивался-таки, разделял шаблон на шаблонную составляющую и обычную. Последняя составляющая обычно гораздо объёмнее шаблонной, так что оно того стОило

Для разделения применялись все нечестные приёмы: преобразование указателя к void* , указатель на коллбэк, так же преобразованный к void*, а где надо - приводимый к нужному указателю
Автор: daorus 31.10.2010, 11:55
Iron Bug, во-первых, привет земляку. Я тоже из Екатеринбурга.
Цитата
но в примере никакой форвард-декларации нет
Форвард-декларация - это такой способ сэкономить время на компилции. Форвард декларация - это простое сообщение о существовании сигнатуры. А декларация - это подробное разъяснение, что за этой сигнатурой скрывается. Форвард-деклараци - это любая функция или класс, в синтаксисе которых отсутствуют фигурные скобки - это основной признак форвад-декларации. Исторически появились как необходимость кросс-вставки классов.
class A { B b }; class B { A a }; // error
// вот и появилась форвард-декларация
class B; class A { B b; }; class B { A a; }; // ok
// на самом деле тоже не ок, для ок'я нужны указатели, см. ниже
Ну а сегодня, её юзают тупо для сокращения времени компиляции, если есть возможность обойтись одними указателями на класс или функцию, потому что размер указателя вне зависимости от типа есть константа.
class B; class A { B* b; }; // ok
class B; class A { B b; }; // error
class B{}; class A { B b; }; // ok
Топик начался с того, что для шаблонных классов создание обычной декларации не катит, а как катит я не знал. И тут мне на форуме подсказали супер-мегафичу, которую я домыслил и оказалось, что для шаблонов форвард-декларацией является как бы создание экземлпяра шаблона! + кстати, подсказанный пример можно существенно сократить, выкинув сигнатуры классов в шаблоне KEY, T. Главное - сохранить кол-во классов в шаблоне.
class a; // обычный форвард
template<class, class> b; // шаблонный форвард
class A { a* ptr; }; // юз обычного класса
class B { b<a,a>* ptr; }; // юз шаблонного
Автор: Iron Bug 31.10.2010, 12:08
Цитата(daorus @ 31.10.2010, 13:55)

Исторически появились как необходимость кросс-вставки классов.
воот. так что кроме ссылок на самого себя и циклических ссылок это, конечно, можно, но нафиг нигде не нужно. ибо всё,что нужно компилеру - это увидеть описание класса до его использования в коде, что, в случае простого использования класса, достигается вставкой инклюда в одном месте кода, как правило в заголовочнике, где он используется. а в обычном случае, без циклических ссылок, предварительные объявления без инклюда, тем более для стандартно или библиотечно определённых шаблонов, ни к чему, кроме ещё одного лишнего прохода при компиляции, не приводят. куда ты его вставишь - дело десятое. если класс сам по себе используется, то компилятор обработает его один раз. так что "экономия" времени компиляции будет только в случае библиотеки, которая оперирует только указателями и никогда не обращается к самому классу.
Цитата(Алексей1153 @ 31.10.2010, 13:34)

о да. Столкнулся несколько раз, пытался решить, спрашивал советы - так и оставил в заголовочнике весь шаблон.
я как-то тоже напоролась на это. это баг мелкософта. об этом написано где-то в MSDN, только мелким-мелким шрифтом на страничке, которую так просто и не найти

там порядка восьми несовместимостей со стандартом у них имеется. в прочих компилерах этого нет.
Цитата(daorus @ 31.10.2010, 13:55)

Топик начался с того, что для шаблонных классов создание обычной декларации не катит, а как катит я не знал. И тут мне на форуме подсказали супер-мегафичу, которую я домыслил и оказалось, что для шаблонов форвард-декларацией является как бы создание экземлпяра шаблона! + кстати, подсказанный пример можно существенно сократить, выкинув сигнатуры классов в шаблоне KEY, T. Главное - сохранить кол-во классов в шаблоне.
всё объясняется проще: когда мелкософтовский компилер собирает модули, он считает, что если нет экземпляра шаблонного класса - то его описание можно просто выкинуть. это и есть баг компилера, его несоответствие стандарту. просто для MSVC нужно, чтобы либо инклюд был явно включен в файл, где шаблон будет использован, либо вот так вот, как ты описал, извратиться и заставить компилер учесть шаблонный класс методом создания фиктивного объекта.
Автор: Алексей1153 31.10.2010, 12:52
экономия времени будет. По крайней мере в студии это очень заметно 
Например, класс A (не шаблон), описанный в хедере, не используется в некоем cpp, но из-за вложенности хедеров попал в начало этого cpp. Компилятор его лишний раз скомпилирует (потому что это только у шаблонов не компилируется неиспользыемое).
В случае же разнесения кода по A.h и A.cpp , компиляция пройдёт шустрее, так как компилироваться класс A будет всего один раз.
Проверить можно так: пусть класс A целиком сидит в A.cpp
Можно написать в любой его функции строчку
{int i;} //компилятор выдаст предупреждение, что не использована переменная
Везде, где инклуд A.h используется, предупреждение появится (ровно столько же раз, сколько заинклужено)
Автор: igor_bogomolov 31.10.2010, 13:34
Цитата(Iron Bug @ 31.10.2010, 11:08)

с MSVC, то нельзя выносить реализацию шаблонного класса из его объявления (это баг MSVC, несовместимость со стандартами). то есть, в заголовочнике должен быть весь класс
Цитата(Алексей1153 @ 31.10.2010, 11:34)

так и оставил в заголовочнике весь шаблон
При чем здесь MSVC? Разделить реализацию и объявление шаблона можно только с использованием ключевого слова
export, которое ни один компилятор так и не реализовал. Поэтому для шаблонов подходит только
модель включения, т.е. в одном файле должны быть все объявления и определения шаблона. У http://www.rsdn.ru/res/book/cpp/Template.xml это хорошо описано.
Цитата(daorus @ 31.10.2010, 11:55)

template<class, class> b; // шаблонный форвард
Тут ошибка, должно быть template<class, class>
class b;
Кстати, по поводу того зачем нужны форвард объявления хорошо описано в этой книге (http://www.rsdn.ru/res/book/cpp/cplusplusckbk.xml) (см. раздел 2.3)
--Добавлено чуть позже--------------------------------------------------------------------------
Этого вообще не понял
Цитата(Iron Bug @ 31.10.2010, 12:08)

всё объясняется проще: когда мелкософтовский компилер собирает модули, он считает, что если нет экземпляра шаблонного класса - то его описание можно просто выкинуть. это и есть баг компилера, его несоответствие стандарту. просто для MSVC нужно, чтобы либо инклюд был явно включен в файл, где шаблон будет использован, либо вот так вот, как ты описал, извратиться и заставить компилер учесть шаблонный класс методом создания фиктивного объекта.
Разберем по частям
Цитата
когда мелкософтовский компилер собирает модули, он считает, что если нет экземпляра шаблонного класса - то его описание можно просто выкинуть.
совершенно правильно считает
Цитата
просто для MSVC нужно, чтобы либо инклюд был явно включен в файл, где шаблон будет использован
а как вы собираетесь использовать класс, если в данном листе трансляции ничего про него не известно? Естественно нужно делать инклуд, и шаблоны тут не при чем
Цитата
либо вот так вот, как ты описал, извратиться и заставить компилер учесть шаблонный класс методом создания фиктивного объекта
какой еще фиктивный объект? Где вы его увидели? Там лишь форвард объявление шаблонного класса, никаких объектов не создается
Автор: daorus 31.10.2010, 13:47
Цитата(Iron Bug @ 31.10.2010, 14:08)

достигается вставкой инклюда в одном месте кода
У меня почему-то активное использование форвардов существенно экономит время компиляции. + если зависимость от форварда, то изменение в заголовочном файле не генерит флаг на перекомпиляцию. А вообще это просто вопрос привычки. Привык я форвары в хедерах юзать вместо инклудов. :-)
Цитата(Iron Bug @ 31.10.2010, 14:08)

всё объясняется проще: когда мелкософтовский компилер
а я на g++ работаю. Про msvc вообще ничего не знаю.
Цитата(igor_bogomolov @ 31.10.2010, 15:13)

Тут ошибка, должно быть template<class, class> class b;
Да, конечно. Я слишком увлекся идеей. Но почему-то у меня нет кнопки правка сообщений, поэтому баг так и останется висеть в моем посте. :-(
Цитата(igor_bogomolov @ 31.10.2010, 15:34)

с использованием ключевого слова export
Если я правильно понял, это вы уже не про хедеры, а про форварды в *.cpp файлах?
т.е. типа такого
// my.cpp
export template<class, class> class B;
my::my() { B<int, int> b; }
Вы это имели ввиду?
Автор: Алексей1153 31.10.2010, 13:50
Цитата(daorus @ 31.10.2010, 16:41)

Привык я форвары в хедерах юзать вместо инклудов. :-)
это не должно быть самоцелью. Частенько в этом нет необходимости, но ведёт к затратам времени на разработку плюс учитывание ручного управления памятью. Причины использовать форвард - это кросс-ссылка или большой объём кода. Других причин не вижу ))
Автор: igor_bogomolov 31.10.2010, 14:09
Цитата(daorus @ 31.10.2010, 13:47)

Но почему-то у меня нет кнопки правка сообщений
Появится после 20 сообщений.
Цитата(daorus @ 31.10.2010, 13:47)

Если я правильно понял, это вы уже не про хедеры, а про форварды в *.cpp файлах?
Это я про разделение объявления шаблонного класса и его реализации по .cpp/.h фалам http://www.cppreference.com/wiki/keywords/export (если конечно я правильно идею понял, попробовать, как вы понимаете, это не получится)
Цитата
Привык я форвары в хедерах юзать вместо инклудов. :-)
Аналогично. Если в классе объявленны только ссылки и указатели, не вижу ни какого смысла делать инклуд, только форвард объявление
Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)