С++, почему следующая переменная не проходит через виртуальную таблицу

Предположим, у нас есть такой код:

struct X{
   virtual void foo(){}
};

struct Y: X{
   void foo() override{}
}

И мы делаем что-то вроде этого:

void test(Y& ref){
   Y local_y;
   local_y.foo();
   ref.foo();
}

В первом вызове (в каком-то компиляторе) первый вызов вообще не будет проходить через vptr, а второй будет.

Это имело смысл для меня, потому что, насколько я знаю, только ссылки и указатели полиморфны в С++, а обычные переменные - нет, поэтому нет необходимости просматривать таблицу.

Однако причиной этого было то, что это оптимизация компилятора, потому что local_y находится в стеке. Таким образом, компилятор знал, что не нужно проходить через виртуальную таблицу.

Хотя причины похожи (а может быть и те же), мне интересно - имеет ли смысл то, что я думал? Кроме того, будет ли у local_y вообще vptr? (Я предполагаю, что ответ положительный, потому что мы можем сделать Y& new_y=local_y, а new_y должен иметь этот указатель).

Я советую вам перестать зацикливаться на деталях реализации. Если компилятору не нужно ничего делать, он ничего не сделает. Ваш код описывает программу. Вы не программируете компьютер; это работа вашего компилятора.

Asteroids With Wings 21.12.2020 20:42

Зачем ref.foo() проходить через vptr? Из вашего примера не похоже, что Y является полиморфным классом. Вы имели в виду void test(X& ref) {?

scohe001 21.12.2020 20:42

В обоих случаях должна вызываться правильная функция foo. Как компилятор в конечном итоге вызывает это, является внутренней деталью реализации. Неважно, пройдет ли он через vptr или «не пройдет», а стандарт языка C++ даже не определяет и не требует «vptr».

dxiv 21.12.2020 20:44

@ scohe001 Чем Y не является полиморфным классом? А что, если Y& ref относится к Z?

Asteroids With Wings 21.12.2020 21:02

Ах, похоже, наследование полиморфной функции делает вас полиморфным (en.cppreference.com/w/cpp/language/object#Polymorphic_objec‌​ts). Я не знал об этом. Хороший звонок @Asteroids.

scohe001 21.12.2020 21:07

@AsteroidsWithWings Ну, у меня есть класс, который фокусируется именно на этих вещах, так что это не совсем мой выбор.

EL_9 21.12.2020 21:08

@EL_9 Мне жаль это слышать :(

Asteroids With Wings 22.12.2020 21:44
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
7
110
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это имело смысл для меня, потому что, насколько я знаю, только ссылки и указатели полиморфны в С++, а обычные переменные - нет, поэтому нет необходимости просматривать таблицу.

имеет ли смысл то, что я подумал?

Нет. Полиморфизм среды выполнения каким-то образом не «отключен» для «обычных переменных». Виртуальная функция — это виртуальная функция, и при вызове виртуальной функции применяется полиморфизм времени выполнения. Период.

Люди любят говорить, что «полиморфизм времени выполнения работает только со ссылками и указателями», потому что синтаксически трудно найти контрпримеры, не отсекая производные части вашего объекта.

В вашем случае вам даже не нужна виртуальная диспетчеризация (вы буквально просто вызываете функцию, которую хотите вызвать), поэтому весь вопрос спорный.

Однако причиной этого было то, что это оптимизация компилятора, потому что local_y находится в стеке. Таким образом, компилятор знал, что не нужно проходить через виртуальную таблицу.

Это имеет больше смысла. Тривиально доказуемо, что объект с именем local_y имеет динамический тип Y, потому что его объявление находится прямо в функции. Так что нет необходимости в каких-либо магических выходках, кроме буквального вызова функции foo(), которая определена в Y.

Вы можете назвать это «оптимизацией»; Я называю это "компилятор делает свою работу без лишних шагов".

Будет ли у local_y вообще vptr?

Возможно, но только потому, что компилятор мало что выиграет, выборочно изменяя части определений классов на основе локального использования.

Наличие или отсутствие vptr зависит от настроек оптимизации. При -O2 и выше он даже не генерирует y_local не говоря уже о vptr. См. godbolt.org/z/7cbe3P

Warren 22.12.2020 01:53

Будет ли объект, который не является указателем или ссылкой, проходить через свою динамическую таблицу, когда для него вызывается функция?

EL_9 31.12.2020 15:14

@ EL_9 Нет, и поэтому люди ошибочно думают, что полиморфизм «работает только» с указателями. Правда в том, что это необходимо только с указателями. Если динамический тип известен статически, никакой магии не требуется. Также имейте в виду, что vtables — это деталь реализации.

Asteroids With Wings 31.12.2020 16:34

Другие вопросы по теме