Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Упреждающее объявление (forward declaration) для шаблонных классов
Форум на CrossPlatform.RU > Разработка > С\С++
daorus
Какой синтаксис форвад-декларации для типа хеш, если я хочу в классе хранить на него указатель

// 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
помести
#include <QHash>
#include <QString>
в заголовочный файл

ну а если поизвращаться всё равно надо, то сделай класс-оболочку, а вот его уже форвардни :)
Litkevich Yuriy
Здесь для QString этот номер не пройдёт, т.к. используются не указатели на него.

Вообще forward declaration возможен только в случае применения указателей. Т.к. их размер (в памяти) постоянен
kwisp
интересный вопрос. что на данный вопрос отвечает гугл?
просто так пальцем в небо.
template <>
class QHash<QString, QString>;
не схавает?
а вообще конечно - такого рода штуки могут быть непереносимы так что, можно и отказаться от такого рода forward declaration

Цитата(Litkevich Yuriy @ 30.10.2010, 21:30) *
в случае применения указателей.

а ссылок?
Litkevich Yuriy
Цитата(kwisp @ 30.10.2010, 23:33) *
а ссылок?
тут не уверен, возможно, тоже пройдёт
Алексей1153
ссылка - также, как и указатель. Потому что это одно и тоже для компилятора :)
igor_bogomolov
Цитата(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
Игорь, сильно сомневаюсь

template <class Key, class T>
class QHash;

QHash<QString, QString>* dic;

а ты проверил, компилится ?

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

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

====

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

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

о да. Столкнулся несколько раз, пытался решить, спрашивал советы - так и оставил в заголовочнике весь шаблон.
Но душа то не удовлетворена ))) Изворачивался-таки, разделял шаблон на шаблонную составляющую и обычную. Последняя составляющая обычно гораздо объёмнее шаблонной, так что оно того стОило :) Для разделения применялись все нечестные приёмы: преобразование указателя к void* , указатель на коллбэк, так же преобразованный к void*, а где надо - приводимый к нужному указателю
daorus
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
Цитата(daorus @ 31.10.2010, 13:55) *
Исторически появились как необходимость кросс-вставки классов.

воот. так что кроме ссылок на самого себя и циклических ссылок это, конечно, можно, но нафиг нигде не нужно. ибо всё,что нужно компилеру - это увидеть описание класса до его использования в коде, что, в случае простого использования класса, достигается вставкой инклюда в одном месте кода, как правило в заголовочнике, где он используется. а в обычном случае, без циклических ссылок, предварительные объявления без инклюда, тем более для стандартно или библиотечно определённых шаблонов, ни к чему, кроме ещё одного лишнего прохода при компиляции, не приводят. куда ты его вставишь - дело десятое. если класс сам по себе используется, то компилятор обработает его один раз. так что "экономия" времени компиляции будет только в случае библиотеки, которая оперирует только указателями и никогда не обращается к самому классу.

Цитата(Алексей1153 @ 31.10.2010, 13:34) *
о да. Столкнулся несколько раз, пытался решить, спрашивал советы - так и оставил в заголовочнике весь шаблон.

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

Цитата(daorus @ 31.10.2010, 13:55) *
Топик начался с того, что для шаблонных классов создание обычной декларации не катит, а как катит я не знал. И тут мне на форуме подсказали супер-мегафичу, которую я домыслил и оказалось, что для шаблонов форвард-декларацией является как бы создание экземлпяра шаблона! + кстати, подсказанный пример можно существенно сократить, выкинув сигнатуры классов в шаблоне KEY, T. Главное - сохранить кол-во классов в шаблоне.

всё объясняется проще: когда мелкософтовский компилер собирает модули, он считает, что если нет экземпляра шаблонного класса - то его описание можно просто выкинуть. это и есть баг компилера, его несоответствие стандарту. просто для MSVC нужно, чтобы либо инклюд был явно включен в файл, где шаблон будет использован, либо вот так вот, как ты описал, извратиться и заставить компилер учесть шаблонный класс методом создания фиктивного объекта.
Алексей1153
экономия времени будет. По крайней мере в студии это очень заметно :)

Например, класс A (не шаблон), описанный в хедере, не используется в некоем cpp, но из-за вложенности хедеров попал в начало этого cpp. Компилятор его лишний раз скомпилирует (потому что это только у шаблонов не компилируется неиспользыемое).

В случае же разнесения кода по A.h и A.cpp , компиляция пройдёт шустрее, так как компилироваться класс A будет всего один раз.

Проверить можно так: пусть класс A целиком сидит в A.cpp

Можно написать в любой его функции строчку
{int i;} //компилятор выдаст предупреждение, что не использована переменная


Везде, где инклуд A.h используется, предупреждение появится (ровно столько же раз, сколько заинклужено)
igor_bogomolov
Цитата(Iron Bug @ 31.10.2010, 11:08) *
с MSVC, то нельзя выносить реализацию шаблонного класса из его объявления (это баг MSVC, несовместимость со стандартами). то есть, в заголовочнике должен быть весь класс
Цитата(Алексей1153 @ 31.10.2010, 11:34) *
так и оставил в заголовочнике весь шаблон
При чем здесь MSVC? Разделить реализацию и объявление шаблона можно только с использованием ключевого слова export, которое ни один компилятор так и не реализовал. Поэтому для шаблонов подходит только модель включения, т.е. в одном файле должны быть все объявления и определения шаблона. У Вандевурда & Джосатиса это хорошо описано.

Цитата(daorus @ 31.10.2010, 11:55) *
template<class, class> b; // шаблонный форвард
Тут ошибка, должно быть template<class, class> class b;

Кстати, по поводу того зачем нужны форвард объявления хорошо описано в этой книге (ссылка) (см. раздел 2.3)

--Добавлено чуть позже--------------------------------------------------------------------------

Этого вообще не понял
Цитата(Iron Bug @ 31.10.2010, 12:08) *
всё объясняется проще: когда мелкософтовский компилер собирает модули, он считает, что если нет экземпляра шаблонного класса - то его описание можно просто выкинуть. это и есть баг компилера, его несоответствие стандарту. просто для MSVC нужно, чтобы либо инклюд был явно включен в файл, где шаблон будет использован, либо вот так вот, как ты описал, извратиться и заставить компилер учесть шаблонный класс методом создания фиктивного объекта.

Разберем по частям
Цитата
когда мелкософтовский компилер собирает модули, он считает, что если нет экземпляра шаблонного класса - то его описание можно просто выкинуть.
совершенно правильно считает
Цитата
просто для MSVC нужно, чтобы либо инклюд был явно включен в файл, где шаблон будет использован
а как вы собираетесь использовать класс, если в данном листе трансляции ничего про него не известно? Естественно нужно делать инклуд, и шаблоны тут не при чем

Цитата
либо вот так вот, как ты описал, извратиться и заставить компилер учесть шаблонный класс методом создания фиктивного объекта
какой еще фиктивный объект? Где вы его увидели? Там лишь форвард объявление шаблонного класса, никаких объектов не создается
daorus
Цитата(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
Цитата(daorus @ 31.10.2010, 16:41) *
Привык я форвары в хедерах юзать вместо инклудов. :-)

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

Цитата(daorus @ 31.10.2010, 13:47) *
Если я правильно понял, это вы уже не про хедеры, а про форварды в *.cpp файлах?
Это я про разделение объявления шаблонного класса и его реализации по .cpp/.h фалам http://www.cppreference.com/wiki/keywords/export (если конечно я правильно идею понял, попробовать, как вы понимаете, это не получится)

Цитата
Привык я форвары в хедерах юзать вместо инклудов. :-)
Аналогично. Если в классе объявленны только ссылки и указатели, не вижу ни какого смысла делать инклуд, только форвард объявление :)
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.