Обязательно ли макет объекта 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, То, что я ищу, является гарантией (явной или неявной) для такого поведения в стандарте. В качестве альтернативы, будет также принято достаточно подробное описание степени компоновки хранилища объектов, предоставляемой стандартом в качестве доказательства того, что такое поведение не гарантируется.

3 ответа

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

Например:

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

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

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

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

Также см., Что поставщик реализовал макрос смещения https://en.cppreference.com/w/cpp/types/offsetof

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

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

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

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

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

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

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

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

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

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

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