Хороший ответ Рика, но, если подвести итог, вы столкнулись с последствиями двух основных принципов проектирования этой функции:
- если вы запрашиваете динамическое связывание, тогда вы получаете динамическое связывание .
- динамический - это просто объект в смешной шляпе .
Первая выявленная вами проблема является следствием первого принципа проектирования. Вы попросили отложить анализ звонка до времени выполнения. Компилятор так и сделал. Это включает в себя отсрочку всего о вызове до времени выполнения, включая разрешение перегрузки и определение типа возврата. Тот факт, что у компилятора достаточно информации, чтобы догадаться о том, что вы имели в виду, не имеет значения.
И если компилятор действительно догадался о том, что вы имели в виду, то сейчас вы зададите другой вопрос, а именно: «Я внес небольшое изменение в набор доступных методов, и вдруг компилятор изменил свой вывод тип динамический, почему? Это очень сбивает с толку пользователей, когда поведение компилятора непредсказуемо.
(Все это говорит о том, что есть небольшое количество ситуаций, в которых компилятор скажет вам, что динамический код неправильный. Есть ситуации, когда мы знаем, что динамическое связывание будет всегда терпеть неудачу во время выполнения, и мы можем рассказать вам о них во время компиляции, а не ждать, пока ваш тестовый сценарий потерпит неудачу.)
Вторая выявленная вами проблема является следствием второго принципа проектирования. Поскольку динамический объект представляет собой просто забавную шляпу, а поскольку в качестве значения NULL указывается пустая ссылка или тип значения, не имеющий значения NULL, в штучной упаковке не существует такого понятия, как «динамическое значение NULL».