Определен ли статический объект C++?

Более конкретно, если предположить, что A является доступным базовым классом B, делает ли следующий код неопределенное поведение, и является ли утверждение гарантированным, чтобы оно не срабатывало в соответствии со стандартом?

void test(B b1, B b2) {
  A* a2 = &b2;
  auto offset = reinterpret_cast<char*>(a2) - reinterpret_cast<char*>(&b2);
  A* a1 = reinterpret_cast<A*>(reinterpret_cast<char*>(&b1) + offset);
  assert(a1 == static_cast<A*>(&b1));
}

Изменить: я знаю, что все распространенные поставщики компиляторов реализуют макет объекта C++ (даже при учете виртуального наследования) таким образом, который совместим с неявными предположениями test. То, что я ищу, - это гарантия (неявная или явная) для этого поведения в стандарте. В качестве альтернативы также будет принята достаточно подробное описание степени гарантии размещения хранилища объектов, предоставляемой стандартом, в качестве доказательства того, что это поведение не гарантируется.

Ответ 1

Это может быть хорошо. При определенных условиях:

A не является (частью) virtual базы, либо b1 и b2 имеют один и тот же самый производный тип, или вам, оказывается, повезло (un-).

Изменить: ваше изменение от пропущенного ссылки к значению по-умолчанию делает тривиальным, чтобы показать условие выше трюмов.

Правила псевдонимов не будут мешать, так как единственным неправильным типом является char, и для этого существует явное исключение.

Ответ 2

Если, например, нет. тип стандартного макета, трудно понять, как реализация должна быть ограничена в этом смысле. Может ли реализация использовать какой-то динамический поиск для базового объекта, например? в теории, я думаю, да. (Опять же, на практике мне трудно понять, какая польза от смещения должна быть статичной и иметь дополнительные накладные расходы)

Например:

Нестатические члены данных класса (non-union) с тем же контролем доступа (раздел 14) распределяются таким образом, что более поздние члены имеют более высокие адреса в объекте класса. Порядок распределения нестатических членов данных с различным контролем доступа не указан (пункт 14). Требования к выравниванию реализации могут привести к тому, что два соседних элемента не будут распределены сразу друг за другом; поэтому могут потребоваться пространство для управления виртуальными функциями (13.3) и виртуальными базовыми классами (13.1).

Стандарт не гарантирует, например, что-либо по отношению к виртуальным базовым классам.

Объект трехмерного копирования или типа стандартного макета (6.7) должен занимать непрерывные байты хранения.

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

Кроме того, см. Реализацию встроенного макроса макроса https://en.cppreference.com/w/cpp/types/offsetof

Хотя только для переменных-членов, даже здесь, довольно ясно, что многое не так много.

Как вы можете видеть, большинство решений остается для реализации.

Также см. Этот ответ (не тот же вопрос, но связанный): C++ Стандарт по адресу унаследованных членов

Ответ 3

Нет, по какой-либо причине, которая не имеет ничего общего с производными классами или reinterpret_cast: арифметика указателей не гарантирует возврата исходного ответа за пределы контекста массива. См. 5.7.4-5 (expr.add), которые указывают, когда они действительны для добавления/вычитания указателей:

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

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