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

11

Решение

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

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

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

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

1

Другие решения

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

Например:

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

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

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

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

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

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

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

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

1

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

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

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

1
По вопросам рекламы [email protected]