Ну, никто на самом деле прямо не ответил на ваш вопрос (ну, тяжело, постарался), так что я буду.Некоторые другие «ответы» здесь на самом деле более полезны для решения вашей проблемы.
Проблема в том, что void DoIt (B *) НЕ является переопределением виртуальной функции DoIt (A *).Это перегрузка .Существует огромная разница.
Когда вы говорите, что DoIt (B *) не вызывается, когда вы передаете B *, я должен предположить, что вы держите ссылки или указатели на вас Q через указатель на что-то более высокоевверх по иерархии.В этих случаях статическое разрешение имен находит только DoIt (A *), и, поскольку B * is-a A *, оно выгружается, и именно эта версия вызывается.Поскольку он является виртуальным, вызывается переопределение в Q.
Если у вас есть указатель на Q в качестве указателя на Q, и вы вызываете DoIt с помощью B *, должна вызываться функция DoIt (B *).,На этом этапе двойная диспетчеризация не требуется и не используется.
Двойная диспетчеризация требуется, когда у вас есть два абстрактных типа и функция, которая должна вести себя по-разному в зависимости от конкретных типов обеих абстракций.Это то, что вы пытаетесь сделать, когда вы вызываете DoIt с B на Q на более высоком уровне, чем обеспечивает статическое именование.Существует слишком много методов, которые отвечают различным потребностям, чтобы иметь возможность предложить одно решение вместо другого в вашем случае, не знаю точно, что вы пытаетесь решить.На самом деле, вам это может даже не понадобиться!Лучшим подходом для вас может быть реализация DoIt (B *) как виртуальной функции в верхней части вашей иерархии.
Я бы посоветовал вам взять книгу Андре Александреску «Современный дизайн C ++» и просмотреть ее.Он объясняет, чертовски крутая реализация посетителя, а также механизм множественной отправки, который масштабируется.Но не останавливайтесь на этом, есть и другие замечательные реализации, которые могут ответить на вопрос по-другому.
Удачи.