Смещение байтов подкласса во время компиляции с виртуальным наследованием

#c #gcc #visual-c #c 17 #sfinae

Вопрос:

Можно ли вычислить во время компиляции смещение в байтах виртуальной базы в иерархии наследования?

Пример —

 class A {};
class B : public virtual A {};
class C : public virtual A {};
class D : public virtual B, public virtual C {};
 

Я хотел бы вычислить смещение в байтах экземпляра B , C , и D w.r.t. A во время компиляции, т. е. без использования какого-либо реального экземпляра любого из этих классов.

Из того, что я до сих пор читал в Интернете, компиляторы реализуют логику (используемую static_cast и dynamic_cast) с помощью виртуальных таблиц, и, поскольку нет истинного экземпляра любого из этих классов, нет виртуальной таблицы, к которой можно было бы привязаться. Но надеюсь, что есть какая-то шаблонная магия, которая может заставить это работать.

Цель — Я пытаюсь заставить эту пользовательскую реализацию RTTI работать с виртуальным наследованием.

Комментарии:

1. @S. M.: С Уважением К.

Ответ №1:

Можно ли вычислить во время компиляции смещение в байтах виртуальной базы в иерархии наследования?

Нет, потому что это по-разному для разных экземпляров объектов. Это не свойство класса.

Давайте рассмотрим ваш пример. B имеет виртуальный базовый класс A . Поэтому B должно быть какое-то смещение байта в an A . То же самое верно и для C .

Однако D наследует виртуальное A через оба B и C . Весь смысл virtual наследования заключается в том, что если вы наследуете один и тот же базовый класс, то все пути для достижения этого базового класса будут ссылаться на один и тот же подобъект динамического типа. Таким D образом, есть только один экземпляр A .

Но где же он? Смещения, которые B и C используются, не могут быть одновременно правильными. Если не… само смещение хранится в таблице vtable.

Именно так (в основном) работает виртуальное наследование. Когда вы создаете a D , D именно он отвечает за назначение того, куда направляются все virtual унаследованные объекты ly. Эти смещения будут отличаться от тех, когда вы создаете объект, динамическим типом которого является B или C .

Нет никакого способа сделать то, что ты пытаешься сделать. Не без фактического создания экземпляров объектов. Хотя C 20 позволяет создавать экземпляры виртуальных типов во время компиляции (при условии, что у них есть жизнеспособные constexpr конструкторы, поэтому вы не можете делать это со всем), вы все равно не можете делать то, что вам нужно. Если у вас есть указатели на два разных типа, чтобы вычислить смещение в байтах между ними, вам нужно reinterpret_cast преобразовать эти указатели в целые числа или unsigned char* числа s. И reinterpret_cast запрещено во время компиляции (и bit_cast не constexpr запрещено, когда задан тип указателя или тип, содержащий указатель).

Комментарии:

1. Ценю подробное объяснение. Согласно вашему комментарию, D vtable имеет смещение, в котором B и C хранятся. Таким же образом B , а C также знает, где A хранится. A смещение в ‘ B (или C ) может быть не таким, как в D , но смещение по — прежнему является константой для каждого типа-оно не может быть для каждого экземпляра, потому что для каждого типа существует только одна виртуальная таблица. Компилятор вычисляет эти смещения и обновляет таблицу v соответствующего типа. Итак, нет ли способа заставить компилятор раскрыть эти значения во время компиляции?

2. @Shahji: » это не может быть для каждого экземпляра, потому что для каждого типа существует только одна виртуальная таблица». Смещение сохраняется в таблице vtable . Динамический тип класса-это тот, который помещает туда смещение. B и C будет искать смещение в этой таблице v. Это то, что позволяет B и C ссылаться на одно и то же A , когда они находятся в а D . Смещение не может быть «константой для каждого типа»; оно должно определяться на основе используемого динамического типа. Если вы создадите a B , это B будет использовать другое смещение B внутри a D .