Я действительно думаю, что здесь будет неправильно использоваться шаблон посетителей. Вместо этого это классический запах кода включаемых типов, который лучше всего обрабатывается полиморфизмом.
Когда вы говорите «что, если у одного производного класса есть дополнительный метод для вызова», это предполагает конкретный дизайн. Это не функциональное требование. Функциональным требованием было бы «что если один из двух созданных объектов должен был вести себя как X во время события Y». Почему это отличается? Потому что есть ряд способов реализовать это, которые не требуют больше интерфейса (хотя, может быть, больше методов).
Позвольте мне показать пример.
У вас есть фабрика
std::map<ConfigValue, Generator> objectFactory_;
Для которого вы зарегистрировали группу генераторов (вероятно, в конструкторе класса)
RegisterGenerator(configValueA, DummyGenerator);
RegisterGenerator(configValueB, RealGenerator);
...
И в какой-то момент вы хотите создать один из этих объектов.
shared_ptr<Base> GetConfigObject(ConfigFile config)
{
return objectFactory_[config.a]();
}
А затем вы хотите использовать объект для обработки события, вы можете сделать
void ManagingClass::HandleEventA()
{
theBaseObjectReturned->HandleEventAThroughInterfaceObject(this);
}
Обратите внимание, как я передал этот указатель. Это означает, что если у вас есть один объект, который не хочет ничего делать (например, выполнить дополнительный вызов поведения), который может предоставить ваш управляющий класс, он не должен использовать это.
Object<DummyInterface>::HandleEventAThroughInterfaceObject(ManagingClass *)
{
// just do dummy behavior
}
И затем, если вы хотите сделать что-то дополнительное (вызвать новое поведение), оно может сделать это через этот указатель в RealInterface
Object<RealInterface>::HandleEventAThroughInterfaceObject(ManagingClass * that)
{
that->DoExtraBehavior();
// then dummy - or whatever order
// you could even call multiple methods as needed
}
Это основной подход, который вы всегда должны использовать при работе с полиморфизмом. У вас никогда не должно быть двух разных путей кода для разных типов, кроме как через вызовы виртуальной диспетчеризации. У вас никогда не должно быть двух разных блоков кода, один из которых вызывает методы A, B и C, а другой - только A и D при работе с базовым объектом, в зависимости от типа. Вместо этого всегда заставляйте производные объекты выполнять работу, выясняя, что делать - потому что они знают, кто они. Если вам нужно что-то сделать в управляющем объекте, передайте указатель this для работы с ним.