Информация об изменениях

Сообщение Re[36]: Зачем плюс? от 29.11.2018 16:46

Изменено 29.11.2018 21:05 σ

Re[36]: Зачем плюс?
σ>>Насколько я знаю, получить из указателя на T указатель на char в общем случае невозможно

NI>Ну, видимо, подразумевается, что static_cast<cv char *>(static_cast<cv void *>(pointer_to_T)) должно давать валидный указатель на cv char, соответствующий первому байту исходного объекта типа T.


После такого каста у нас остаётся указатель со значением "pointer to T". Не с типом pointer to T, а со значением.
Я думаю ты в курсе этих нововведений C++17 с формализацией, что такое pointer value https://timsong-cpp.github.io/cppwp/n4659/basic.compound#3
Каст к void* не меняет значения указателя https://timsong-cpp.github.io/cppwp/n4659/conv.ptr#2
Каст из void* к любому другому указателю тоже не меняет значение указателя, кроме случая, когда объекты данных типов pointer-interconvertible https://timsong-cpp.github.io/cppwp/n4659/expr.static.cast#13 https://timsong-cpp.github.io/cppwp/n4659/basic.compound#def:pointer-interconvertible

Вообще я думал, что для так называемых strict aliasing правил важно, чтобы у нас было glvalue обозначающее объект типа char (или какого-то другого разрешённого) и проблема в том, что мы не можем его получить. Сейчас перепроверил — это не так. Но проблема тут всё равно есть.

Вот код.
int i = 1488;
char c = *reinterpret_cast<char*>(&i);

Разберём по частям мною написанное. reinterpret_cast<char*>(&i) это prvalue типа 'указатель на char' и, как я выше показал, со значением 'указатель на int'. (Кстати, это пример того, что тип значения prvalue может не совпадать с типом выражения). Теперь смотрим на indirection *reinterpret_cast<char*>(&i):
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points. If the type of the expression is “pointer to T”, the type of the result is “T”. (https://timsong-cpp.github.io/cppwp/n4659/expr.unary.op#1)
Как видно, lvalue тоже может быть типа T, но при этом обозначать объект типа U, т.к. тип выводится из типа выражения, а значение — из значения выражения, которые для prvalue of pointer type могут не совпадать.
Теперь собственно к т.н. strict aliasing rules https://timsong-cpp.github.io/cppwp/n4659/basic.lval#8 :
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

— a char, unsigned char, or std​::​byte type.

В общем, в этом правизе значение имеет не тип значения glvalue, а тип выражения. Т.е. как бы можно, т.к. у нас lvalue of char type.

Но вообще в этой инициазизации выражение попадает под lvalue-to-rvalue conversion. Смотрим, что написано про него https://timsong-cpp.github.io/cppwp/n4659/conv.lval#3
The result of the conversion is determined according to the following rules:
… (тут всё не подходит)
(3.4) — Otherwise, the value contained in the object indicated by the glvalue is the prvalue result.

lvalue *reinterpret_cast<char*>(&i) у нас indicate the int object — i.

Long story short: результат выражения lvalue_to_rvalue_conv(i) такой же, как результат выражения lvalue_to_rvalue_conv(*reinterpret_cast<char*>(&i)).

Не очень-то похоже на доступ к первому байту
Re[36]: Зачем плюс?
σ>>Насколько я знаю, получить из указателя на T указатель на char в общем случае невозможно

NI>Ну, видимо, подразумевается, что static_cast<cv char *>(static_cast<cv void *>(pointer_to_T)) должно давать валидный указатель на cv char, соответствующий первому байту исходного объекта типа T.


После такого каста у нас остаётся указатель со значением "pointer to T". Не с типом pointer to T, а со значением.
Я думаю ты в курсе этих нововведений C++17 с формализацией, что такое pointer value https://timsong-cpp.github.io/cppwp/n4659/basic.compound#3
Каст к void* не меняет значения указателя https://timsong-cpp.github.io/cppwp/n4659/conv.ptr#2
Каст из void* к любому другому указателю тоже не меняет значение указателя, кроме случая, когда объекты данных типов pointer-interconvertible https://timsong-cpp.github.io/cppwp/n4659/expr.static.cast#13 https://timsong-cpp.github.io/cppwp/n4659/basic.compound#def:pointer-interconvertible

Вообще я думал, что для так называемых strict aliasing правил важно, чтобы у нас было glvalue обозначающее объект типа char (или какого-то другого разрешённого) и проблема в том, что мы не можем его получить. Сейчас перепроверил — это не так. Но проблема тут всё равно есть.

Вот код.
int i = 1488;
char c = *reinterpret_cast<char*>(&i);

Разберём по частям мною написанное. reinterpret_cast<char*>(&i) это prvalue типа 'указатель на char' и, как я выше показал, со значением 'указатель на int'. (Кстати, это пример того, что тип значения prvalue может не совпадать с типом выражения). Теперь смотрим на indirection *reinterpret_cast<char*>(&i):
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points. If the type of the expression is “pointer to T”, the type of the result is “T”. (https://timsong-cpp.github.io/cppwp/n4659/expr.unary.op#1)
Как видно, lvalue тоже может быть типа T, но при этом обозначать объект типа U, т.к. тип итогового выражения выводится из типа выражения-операнда, а тип итогового значения — из типа значения выражения-операнда, которые для prvalue of pointer type могут не совпадать.
Теперь собственно к т.н. strict aliasing rules https://timsong-cpp.github.io/cppwp/n4659/basic.lval#8 :
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

— a char, unsigned char, or std​::​byte type.

Главное, чтобы glvalue имело нужный тип, а не то, на что оно ссылается. У нас есть lvalue типа char, т.е. вроде бы можно access the stored value of an object через это lvalue.

При инициазизации переменной к выражению применяется lvalue-to-rvalue conversion. Смотрим, что написано про него https://timsong-cpp.github.io/cppwp/n4659/conv.lval#3
The result of the conversion is determined according to the following rules:
… (тут всё не подходит)
(3.4) — Otherwise, the value contained in the object indicated by the glvalue is the prvalue result.

lvalue *reinterpret_cast<char*>(&i) у нас indicate the int object: i. Значит результат выражения lvalue_to_rvalue_conv(*reinterpret_cast<char*>(&i)) — это значение, хранящееся в объекте i.

Long story short: результат выражения lvalue_to_rvalue_conv(i) такой же, как результат выражения lvalue_to_rvalue_conv(*reinterpret_cast<char*>(&i)).

Не очень-то похоже на доступ к первому байту