Обязательно ли макет объекта C ++ обязательно статически определен? - PullRequest
0 голосов
/ 25 августа 2018

В частности, если предположить, что 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 ]

0 голосов
/ 30 августа 2018

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

Например:

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

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

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

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

Также см. Вендор, реализовавший макрос offsetof https://en.cppreference.com/w/cpp/types/offsetof

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

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

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

0 голосов
/ 02 сентября 2018

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

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

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

0 голосов
/ 25 августа 2018

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

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

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

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

...