Ответ: с языковой точки зрения вызов на bar()
внутри B::foo()
разрешается посредством виртуальной отправки.
Суть в том, что с точки зрения языка C ++ виртуальная диспетчеризация всегда происходит при вызове виртуального метода с использованием его неквалифицированного имени. Когда вы используете квалифицированное имя метода, виртуальная отправка не происходит. Это означает, что единственный способ подавить виртуальную диспетчеризацию в C ++ - использовать этот синтаксис
some_object_ptr->SomeClass::some_method();
some_object.SomeClass::some_method();
В этом случае динамический тип, если объект с левой стороны игнорируется и конкретный метод вызывается напрямую.
Во всех других случаях виртуальная диспетчеризация происходит, если говорить о языке. То есть вызов разрешается в соответствии с динамическим типом объекта. Другими словами, с формальной точки зрения, каждый раз, когда вы вызываете виртуальный метод через непосредственный объект, как в
B obj;
obj.foo();
метод вызывается через механизм «виртуальной отправки» независимо от контекста («внутри виртуального метода» или нет - не имеет значения).
Вот так на языке C ++. Все остальное - просто оптимизации, сделанные компиляторами. Как вы, вероятно, знаете, большинство (если не все) компиляторы будут генерировать не виртуальный вызов виртуального метода, когда вызов выполняется через непосредственный объект. Это, конечно, очевидная оптимизация, так как компилятор знает, что статический тип объекта совпадает с его динамическим типом. Опять же, это не зависит от контекста («внутри виртуального метода» или нет - не имеет значения).
Внутри виртуального метода вызов может быть выполнен без указания объекта с левой стороны (как в вашем примере), что действительно означает, что все вызовы this->
неявно присутствуют слева. Те же самые правила применяются и в этом случае. Если вы просто позвоните bar()
, он будет означать this->bar()
, и вызов будет отправлен практически. Если вы звоните B::bar()
, это означает this->B::bar()
, и вызов отправляется не виртуально. Все остальное будет зависеть только от возможностей оптимизации компилятора.
То, что вы пытаетесь сказать "потому что, как только вы попадаете внутрь B::foo()
, оно точно относится к типу B*
, а не D*
", мне совершенно неясно. Это утверждение упускает суть. Виртуальная отправка зависит от типа dynamic объекта . Примечание: это зависит от типа *this
, а не от типа this
. Неважно, что это за тип this
. Важен динамический тип *this
. Когда вы находитесь внутри B::foo
, все еще вполне возможно, что динамический тип *this
равен D
или что-то еще. По этой причине вызов bar()
должен быть разрешен динамически.