#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
. Смещение не может быть «константой для каждого типа»; оно должно определяться на основе используемого динамического типа. Если вы создадите aB
, этоB
будет использовать другое смещениеB
внутри aD
.