Определен ли статический объект 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), которые указывают, когда они действительны для добавления/вычитания указателей:
Когда выражение с интегральным типом добавляется или вычитается из указателя, результат имеет тип операнда указателя. Если операнд указателя указывает на элемент объекта массива, и массив достаточно велик, результат указывает на смещение элемента от исходного элемента, так что разность индексов результирующих и исходных элементов массива равна интегральному выражению.... Если оба операнда указателя и результат указывают на элементы одного и того же объекта массива или один за последним элементом объекта массива, оценка не должна приводить к переполнению; в противном случае поведение не определено.
Язык для вычитания немного более двусмыслен, но говорит по существу то же самое.