Реализуем ли C ++ 17 на платформах с прямым порядком байтов? - PullRequest
0 голосов
/ 13 января 2019

Давайте посмотрим на следующий код:

int i = 10;
char c = reinterpret_cast<char&>(i);

[expr.reinterpret.cast] / 11

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

Таким образом, значение reinterpret_cast<char&>(i) l с указанным типом char относится к объекту int i.

Чтобы инициализировать c, нам нужно значение, поэтому применяется преобразование lvalue в rvalue [conv.lval] /3.4:

значение, содержащееся в объекте, указанном в glvalue, является результатом prvalue.

Результатом преобразования L2R является значение, содержащееся в объекте i. Пока значение i находится в диапазоне, представленном char ( [expr] / 4 говорит, что в противном случае это UB), переменная c должна быть инициализирована, чтобы иметь то же самое значение.

Из реализации POV на платформе с прямым порядком байтов это легко сделать, прочитав байт по адресу объекта i. Однако на платформе с прямым порядком байтов компилятор должен будет добавить смещение, чтобы получить наименее значимый байт. Или прочитайте весь объект int в регистр и замаскируйте первый байт, что приемлемо для обоих порядков байтов.

Если вы считаете, что приведенный выше код может быть легко обработан компилятором для создания кода, который ведет себя в соответствии со стандартом C ++ 17, подумайте о приведении указателя на int, указывающего на i, в указатель на char. Такое приведение не изменяет значение указателя, т.е. оно все еще указывает на объект int i, что означает, что применение оператора косвенности к такому указателю с последующим преобразованием L2R должно вести себя так, как это было описано выше, т.е. извлекать значение объекта int, если он представлен типом char.

В следующем коде

int i = 10;
f(reinterpret_cast<char*>(&i)); // void f(char*)

должен ли компилятор корректировать адрес i с некоторым смещением, если он не знает, что функция f будет делать со своим аргументом? А также компилятор не знает, что будет передано функции f. Код выше и функция f находятся в разных единицах перевода.

Например, если f разыменовывает указатель для чтения значения через него, он должен получить значение i, как описано выше. Но он также может быть вызван с указателем на реальный объект char, поэтому f не может настроить данный указатель. Это означает, что вызывающая сторона должна настроить указатель. Но что, если f передает указатель на memcpy, чтобы скопировать sizeof(int) байтов в массив символов этого размера и обратно в другой объект int, как это разрешено [basic.types] / 3 ? Нелегко представить, как здесь настроить указатели для соответствия требуемому (как [basic.types] / 3 , так и [conv.lval] /3.4) поведению.

Итак, что делают существующие реализации, если существуют существующие реализации действительно , соответствующие стандарту C ++ 17?

Ответы [ 3 ]

0 голосов
/ 13 января 2019

[intro.object] / 1 говорит, что для неполиморфных объектов «интерпретация найденных в них значений определяется типом выражений (пункт [expr ]) используется для доступа к ним. "

(акцент сделан на самом стандарте, а не на моем)

Как вы заметили, тип такого выражения в вашем случае - char, поэтому компилятору не нужно интерпретировать это значение как значение некоторого объекта типа int.

0 голосов
/ 18 января 2019

Однако на платформе с прямым порядком байтов компилятору придется добавить смещение, чтобы получить наименее значимый байт. Или прочитайте весь объект int в регистр и замаскируйте первый байт, что приемлемо для обоих порядков байтов.

Это было бы верно, только если вы передали значение i. Однако с reinterpret_cast<char&> вы передали адрес, потому что ссылка - это всего лишь другой синтаксис для указателя. Следовательно, c получит значение MSB i, которое равно 0 до sizeof(int) > 1.

То, что вы написали, было неотличимо от:

int i = 10;
char c = *reinterpret_cast<char*>(&i);
0 голосов
/ 13 января 2019

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

... Результат ссылается на тот же объект, что и источник glvalue, но с указанным типом.


значение, содержащееся в объекте, указанном в glvalue, является результатом prvalue.

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

Учитывая вашу интерпретацию, reinterpret_cast (и все, что определено в терминах reinterpret_cast) становится бесполезным, и реализация невозможна не только в системах BE, но и в системах LE (рассмотрим реинтерпретацию между нецелыми типами и char). Таким образом, я не верю, что это подразумевается. Это может рассматриваться как кандидат на сообщение о дефекте.

Путаница может быть связана с недостаточно точным определением для выражений "значение, содержащееся в" , "указан объект" и "Результат относится к одному и тому же объекту" . Разъяснение или переписывание некоторых или всех из них может быть в порядке.

...