Можете ли вы повторно интерпретировать_cast между типами, которые имеют одинаковое представление? - PullRequest
3 голосов
/ 28 октября 2019

Предположим, у нас есть два типа, которые имеют одинаковое представление (одинаковые переменные-члены и базовые классы, в одном и том же порядке)Это действительно (то есть не UB) для reinterpret_cast между ними? Например, это действительно до reinterpret_cast от Mary до Ashley&? А что, если эти два типа полиморфны?

struct Mary {
    int  m1;
    char m2;
};

struct Ashley {
    int  a1;
    char a2;
};

int TryTwins ()
{
    Mary mary = {};

    Ashley& ashley = reinterpret_cast<Ashley&> (mary);
    ashley.a1 = 1;
    ashley.a2 = 2;

    return mary.m1 + mary.m2;
}

Что если мы приведем начало объекта к другому типу, если мы знаем, что тип источника начинается с переменных-членов целевого типа? Например, это допустимо (т.е. не UB)?

struct Locomotive {
    int    engine;
    char   pantograph;
};

struct Train {
    int    engine;
    char   pantograph;
    int*   wagon1;
    int**  wagon2;
    int*** wagon3;
};

int TryTrain ()
{
    Train train = {};

    Locomotive& loc = reinterpret_cast<Locomotive&> (train);
    loc.engine     = 1;
    loc.pantograph = 2;

    return train.engine + train.pantograph;
}

Обратите внимание, что все основные компиляторы рассматривают их как допустимые приведения ( live demo ). Вопрос в том, позволяет ли это язык C ++.

1 Ответ

1 голос
/ 29 октября 2019

[expr.reinterpret.cast] / 11 :

Выражение glvalue типа T1 может быть приведено к типу «ссылка на T2», есливыражение типа «указатель на T1» может быть явно преобразовано в тип «указатель на T2» с помощью reinterpret_­cast. Результат ссылается на тот же объект, что и источник glvalue, но с указанным типом. [...]

Mary и Ashley являются типами объектов, поэтому указатели на них могут преобразовываться друг в друга. Теперь мы получаем lvalue типа Ashley для доступа к базовому Mary объекту.

[basic.lval] / 8 :

Если программа пытается получить доступ к сохраненному значению объекта через glvalue, отличный от одного из следующих типов, поведение не определено:

  • динамический типобъект,

  • cv-квалифицированная версия динамического типа объекта,

  • тип, аналогичный динамическому типу объекта,

  • тип, который является типом со знаком или без знака, соответствующим динамическому типу объекта,

  • тип, который является знаком илитип unsigned, соответствующий cv-квалифицированной версии динамического типа объекта,

  • тип агрегирования или объединения, который включает один из вышеупомянутых типов среди своих элементов или нестатических элементов данных(включая, рекурсивно, элемент или нестатический элемент данных субагрегата или контained union),

  • тип, который является (возможно, cv-квалифицированным) типом базового класса динамического типа объекта,

  • тип char, unsigned char или std​::​byte.

Ни один из этих случаев не охватывает рассматриваемый случай. («Аналог» говорит о cv-квалификации.) Следовательно, неопределенное поведение.

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