3

Я смотрел на это answer, и мне было интересно, если листинг объекта его первому члену с reinterpret_cast и использование результата может быть безопасным в C++.reinterpret_cast от объекта к первому участнику

Давайте предположим, что у нас есть класс А, класс В и экземпляр Ь Б:

class A{ 
public: 
    int i; 
    void foo(){} 
}; 

class B{ 
public: 
    A a; 
}; 

B b; 

Вопрос 1: Безопасно ли использовать b.a так: reinterpret_cast<A*>(&b)->foo()?

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

В моей лекции о доступных ссылках на reinterpret_cast указано, что такое использование должно быть разрешено, так как нет нарушения псевдонимов, однако оно противоречит многим ответам, таким как this one.

Вопрос2: Можно ли использовать b.a следующим образом: static_cast<A*>(static_cast<void*>(&b))->foo()?

ответ

5

Да, поскольку оба класса здесь standard-layout types, вы можете конвертировать между &b и &b.a.

reinterpret_cast<A*>(p) определяется как static_cast<A*>(static_cast<void*>(p)), (5.2.10p7), поэтому оба ваши вопроса эквивалентны.

Для классов стандартной компоновки адрес struct/class совпадает с адресом его первого нестатического элемента (9.2p19). И static_cast от/до void* сохранит адрес (5.2.9p13), что означает, что результат будет действительным.

Если классы не были стандартными макетами, вы не могли положиться на это поведение.

+2

Если вы пишете такие сумасшедшие вещи, не забудьте поставить static_assert на обоих классах is_standard_layout, чтобы убедиться, что в случае дальнейших изменений предположение не будет нарушено. –

+0

+1 для справок. Но OP действительно должен понимать, что это не способ выйти за пределы лаборатории! Маленький маленький виртуальный в B и макет ушел, из-за самого противного UB ... – Christophe

+1

@Christophe Вы абсолютно правы, я не задавал этот вопрос с намерением использовать его. И если кто-то имеет намерение использовать его, то статический_ассерт был бы очень желанным. – Arnaud

4

Формальный ответ: Есть ситуации, когда вы можете это сделать (см. Ответ @interjay).

Практический ответ: Пожалуйста, не делайте этого. В самом деле. В основном, когда прямой путь доступен:

b.a.foo(); 

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

0

Если вы интересны в C++ 98,2003:

Q1 и Q2 одинаковые конструкции.

Ваши типы - POD. Существует гарантия того, что POD не имеет отступов в начале во время instancing .... Но он не существует garantees во время наследования. Так reinterpret_cast небезопасно ... my question about POD layout

«В реальной жизни» это довольно безопасно, потому что большинство компиляторов выполняют распределение памяти при наследовании как http://phpcompiler.org/articles/virtualinheritance.html

Но знать о риске, который объект базового адреса и B базовый адрес объекта может иметь потенциально разные значения.

+0

Вот почему мой вопрос конкретно касается стандартных типов макета, так что у них действительно есть один и тот же базовый адрес. – Arnaud

+0

«Стандартный тип макета» понятия не существует в C++ 2003. Кроме того, я не нашел особого упоминания особенно в «стандартных типах макетов», о которых идет речь) – bruziuz

+0

Что относительно: «класс и его член являются стандартными макетами» – Arnaud