Есть такой код:
data.h
======
class Data : public Object {
public:
typedef boost::intrusive_ptr<Data>;
class Visitor {
public:
virtual ~Visitor() {}
operator()(Measure::Ptr measure) {}
};
virtual void apply_visitor(Visitor &visitor) = 0;
};
measure.h
=========
class Measure : public Data {
public:
typedef boost::intrusive_ptr<Measure> Ptr;
void apply_visitor(Visitor &visitor)
{
visitor(Ptr(this));
}
};
Пояснения:
Класс Object реализует подсчет ссылок и предназначен для использования совместно с boost::intrusive_ptr<>.
Разумеется, этот код не компилируется, т.к. в момент обработки data.h компилятор ничего не знает про Measure::Ptr и, следовательно, не компилируется класс Data::Visitor.
Что делать?
P.S. В operator() класса Visitor можно передавать ссылку на Measure - для этого достаточно в data.h добавить строку class Measure;, но хочется передавать имеено Measure::Ptr.
А почему обязательно virtual? Можно и не-virtual метод вынести в файл реализации - тогда не будет inlining, но вызов будет обычным, без virtual overhead.
Хотя это действительно broken - ни к чему базовому классу знать о конкретных наследниках. А если уж действительно так сильно хочется tight coupling - так и запихай их в один заголовочный файл, и все будет.
>Получается, самое разумное - вынести класс Visitor за пределы класса Data?
Из приведенного кода такое решение напрашивается. Общий контекст не ясен. В каких отношениях состоят Visitor и Data? Поучему-то ведь было решено объявить Visitor в Data?
Visitor - реализация шаблона (паттерна) проектирования "Посетитель". Полное описание есть в GoF.
Если очень кратко, то смысл в следующем. От Data наследуется несколько классов, а не только Measure. Вся эта хренотень - часть сетевой библиотеки, а описанные выше классы соответствуют различным типам сообщений, которые могут быть переданы по сети. Код, который использует библиотеку, при получении данных по сети получает указатель на Data, а дальше должен сам разбираться, что ему пришло и как это обработать. Для этого делается наследник от Visitor и в нем переопределяются операторы вызова, а потом выполняется apply_visitor. В зависимости от фактического типа объекта, вызывается тот или иной вариант оператора ().
Класс Visitor был объявлен внутри класса Data из-за того, что фактически есть еще несколько иерархий классов и там тоже есть свои visitor'ы.
> а при чём и нахрена тут Visitor ?
> просто слово понравилось что-ли ???
Если честно, не понял сути претензий.
В данном случае название отражает назначение класса. Другое дело, что не стоило, его, наверное, помещать внутрь Data.
>Класс Visitor был объявлен внутри класса Data из-за того, что фактически есть еще несколько иерархий классов и там тоже есть свои visitor'ы.
Просто из приведенного тобой кода естественно всего этого не видно :)
Но я бы не стал делать в проекте сущностей с одним и тем же именем. Если только какие-нибудь мелкие утилитарные конструкции, например, енумы ошибок внутри каждого класса.