Re[31]: Зачем плюс?
От: rg45 СССР  
Дата: 29.11.18 14:13
Оценка:
Здравствуйте, N. I., Вы писали:

NI>Установить тип additive-expression, последовательно применяя соответствующие пункты стандарта можно, но только с помощью неформальной интерпретации правил, т.к. стиль описания правил касаемо бинарных операторов весьма далёк от строгого формального описания. В любом случае http://eel.is/c++draft/expr.type#1 однозначно исключает возможность существования каких-либо выражений, имеющих тип int &&.


Вот, что больше всего сбивает с толку меня, в стандарте полно мест, где используется фигура речи "expression of type", при этом нигде не дается определение, что такое "type of expression". Как так-то? Правомерно ли вообще использовать фразу "тип выражения"?
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[33]: Зачем плюс?
От: N. I.  
Дата: 29.11.18 14:27
Оценка:
σ:

σ>Выражения ссылочного типа в каком-то смысле существуют


Вот только к этому ссылочному типу нельзя применять никакие правила, за исключением того самого, которое определяет реальный тип и value category после adjustment. Реальный тип выражения, к которому можно применять остальные правила, никогда не бывает ссылочным.

σ>В стандарте вполне есть места, которые не имеют смысла. Например, некоторые из так называемых string aliasing правил — мёртвые правила. Доступ к любому объекту по указателю на char разрешён, только воспользоваться этим разрешением невозможно.


В смысле невозможно?
Re[31]: Зачем плюс?
От: σ  
Дата: 29.11.18 14:29
Оценка:
BFE>Да ну нет же. Вот смотрите:
BFE>http://eel.is/c++draft/expr.type#1

BFE>

BFE>If an expression initially has the type “reference to T” ([dcl.ref], [dcl.init.ref]), the type is adjusted to T prior to any further analysis.

BFE>Если выражение изначально имеет тип “ссылка на T”...
BFE>Написано выражение (expression). Не сущность, не переменная и даже не ссылка, а именно выражение. Если бы выражение не могло иметь тип “reference to T”, то можно было бы написать просто, что всякая сущность типа "ссылка на T" рассматривается как "сущность типа T".

Этот пункт чисто технический. Нужен для упрощения написания стандарта. В некоторых местах возникают выражения, для которых проще представить, что у них может быть ссылочный тип и ввести это правило в одном месте, чем каждый раз применять его в 10 местах.
Например, проще сказать, что "id-expression имеет тип переменной, которую именует" (и тогда тип выражения как бы может быть ссылкой, если переменная — ссылка), чем "если id-expression обозначает переменную с не-ссылочным типом T, то тип выражения T; если с ссылочным — то тип, на который ссылается ссылка".

Короче, "реально" выражений с ссылочным типом нет.

BFE>И вообще, этот пункт практически исключает возможность для выражений различить T и T&&.


Этот пункт исключает возможность ответить на вопрос "какой тип у выражения" как "ссылочный", потому что вопрос "какой тип у выражения" — это анализ выражения, а перед любым анализом ссылочность, даже если бы она реально была, отвалилась бы от типа выражения.
Re[32]: Зачем плюс?
От: N. I.  
Дата: 29.11.18 14:36
Оценка:
Здравствуйте, rg45, Вы писали:

R>Правомерно ли вообще использовать фразу "тип выражения"?


Ну, раз сам стандарт использует "type of an expression", значит, правомерно
Re[34]: Зачем плюс?
От: σ  
Дата: 29.11.18 14:40
Оценка:
σ>>В стандарте вполне есть места, которые не имеют смысла. Например, некоторые из так называемых string aliasing правил — мёртвые правила. Доступ к любому объекту по указателю на char разрешён, только воспользоваться этим разрешением невозможно.

NI>В смысле невозможно?


Насколько я знаю, получить из указателя на T указатель на char в общем случае невозможно (в отдельных случаях, типа тривиального T == char или когда T это standard-layout struct, а первый member у него char и т.п., конечно, возможно).
Re[33]: Зачем плюс?
От: rg45 СССР  
Дата: 29.11.18 15:07
Оценка:
Здравствуйте, N. I., Вы писали:

NI>Здравствуйте, rg45, Вы писали:


NI>Ну, раз сам стандарт использует "type of an expression", значит, правомерно


Так а что это такое?
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[34]: Зачем плюс?
От: σ  
Дата: 29.11.18 15:14
Оценка:
NI>>Ну, раз сам стандарт использует "type of an expression", значит, правомерно
R>Так а что это такое?

Что такое "объект" стандарт тоже не определяет, например. Только говорит, как он создаётся.
В любой аксиоматике есть базовые неопределяемые понятия.

Стандарт говорит, у каких выражений какой будет тип. И как другие сущности в языке ведут себя в зависимости от типа выражения. Этого достаточно. На вопрос "что такое X" можно ответить, только выразив X в терминах более базовых понятий Y1, Y2, …, Yn, если они есть.
Re[29]: Зачем плюс?
От: σ  
Дата: 29.11.18 15:30
Оценка:
BFE> Потом придумали, что было бы не плохо дать программистам возможность преобразовывать обычные объекты во временные.

Объект нельзя "преобразовать во временный". Временный объект или не временный определяется способом, которым он создавался.
Re[35]: Зачем плюс?
От: rg45 СССР  
Дата: 29.11.18 15:40
Оценка:
Здравствуйте, σ, Вы писали:

σ>Что такое "объект" стандарт тоже не определяет, например. Только говорит, как он создаётся.

σ>В любой аксиоматике есть базовые неопределяемые понятия.

σ>Стандарт говорит, у каких выражений какой будет тип. И как другие сущности в языке ведут себя в зависимости от типа выражения. Этого достаточно. На вопрос "что такое X" можно ответить, только выразив X в терминах более базовых понятий Y1, Y2, …, Yn, если они есть.


Ну вот это не есть хорошо, имхо, что такой часто используемый мем как "тип выражения" нужно выводить из каких-то базовых понятий. Затрудняет понимание, множит разночтение.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[35]: Зачем плюс?
От: N. I.  
Дата: 29.11.18 15:47
Оценка:
σ:

σ>Насколько я знаю, получить из указателя на T указатель на char в общем случае невозможно


Ну, видимо, подразумевается, что static_cast<cv char *>(static_cast<cv void *>(pointer_to_T)) должно давать валидный указатель на cv char, соответствующий первому байту исходного объекта типа T.
Re[36]: Зачем плюс?
От: σ  
Дата: 29.11.18 16:46
Оценка:
σ>>Насколько я знаю, получить из указателя на 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)).

Не очень-то похоже на доступ к первому байту
Отредактировано 29.11.2018 21:05 σ . Предыдущая версия .
Re[36]: Зачем плюс?
От: σ  
Дата: 29.11.18 16:53
Оценка:
σ>>Стандарт говорит, у каких выражений какой будет тип. И как другие сущности в языке ведут себя в зависимости от типа выражения. Этого достаточно. На вопрос "что такое X" можно ответить, только выразив X в терминах более базовых понятий Y1, Y2, …, Yn, если они есть.

R>Ну вот это не есть хорошо, имхо, что такой часто используемый мем как "тип выражения" нужно выводить из каких-то базовых понятий. Затрудняет понимание, множит разночтение.


Нужно просто пофиксить места, где стандарт недоговаривает про типы выражений. А определение типа выражения через что-то другое ничего не даст, кроме твоих вопросов "что есть это другое?".
Re[32]: Зачем плюс?
От: B0FEE664  
Дата: 29.11.18 17:07
Оценка:
Здравствуйте, σ, Вы писали:

σ>Короче, "реально" выражений с ссылочным типом нет.

ну как же нет, когда написано, что они есть?
Более того, если выражений с ссылочным типом нет, то как вы объясните следующую строчку из стандарта:
  Скрытый текст
int i;

decltype(auto) x4d = (i); // decltype(x4d) is int&

?

BFE>>И вообще, этот пункт практически исключает возможность для выражений различить T и T&&.

σ>Этот пункт исключает возможность ответить на вопрос "какой тип у выражения" как "ссылочный", потому что вопрос "какой тип у выражения" — это анализ выражения, а перед любым анализом ссылочность, даже если бы она реально была, отвалилась бы от типа выражения.

Ну я же с самого начала пытаюсь уйти от анализа выражения и рассматривать тип не выражения, а получаемого объекта.
И каждый день — без права на ошибку...
Re[33]: Зачем плюс?
От: σ  
Дата: 29.11.18 17:11
Оценка:
BFE>Более того, если выражений с ссылочным типом нет, то как вы объясните следующую строчку из стандарта:

https://timsong-cpp.github.io/cppwp/n4659/dcl.type.simple#4
For an expression e, the type denoted by decltype(e) is defined as follows: if e is an lvalue, decltype(e) is T&, where T is the type of e;

У тебя lvalue типа T == int. Поэтому decltype(e) это T& == int&.
Re[30]: Зачем плюс?
От: B0FEE664  
Дата: 29.11.18 17:26
Оценка:
Здравствуйте, σ, Вы писали:

BFE>> Потом придумали, что было бы не плохо дать программистам возможность преобразовывать обычные объекты во временные.

σ>Объект нельзя "преобразовать во временный". Временный объект или не временный определяется способом, которым он создавался.
Да, я согласен, но хочу заметить, что тут мы приходим к проблеме Есть ли жизнь после перемещения?
Автор: rg45
Дата: 20.11.18

Зададимся вопросом: объект получаемый в результате std::move(i) временный или нет? По стандарту — нет. Я считаю, что это идеологическая ошибка развития языка. IMHO, было бы логично считать, что i в результате такого вызова становится временной переменной в том смысле, чтобы рассматривать записи int i{1}; f(std::move(i)); и f(int(1)); как эквивалентные. Но нет.
И каждый день — без права на ошибку...
Re[34]: Зачем плюс?
От: B0FEE664  
Дата: 29.11.18 17:49
Оценка:
Здравствуйте, σ, Вы писали:

BFE>>Более того, если выражений с ссылочным типом нет, то как вы объясните следующую строчку из стандарта:

σ>https://timsong-cpp.github.io/cppwp/n4659/dcl.type.simple#4
σ>For an expression e, the type denoted by decltype(e) is defined as follows: if e is an lvalue, decltype(e) is T&, where T is the type of e;

Ах ну да, специальное правило...
Значит всё-таки с decltype не получится.
Собственно, я думаю, что доказать не получится. Если ссылки на T рассматриваются как T, а потом T по специальным правилам переводится обратно в ссылки, то шансов нет.
И каждый день — без права на ошибку...
Re[37]: Зачем плюс?
От: rg45 СССР  
Дата: 29.11.18 17:53
Оценка:
Здравствуйте, σ, Вы писали:

σ>Нужно просто пофиксить места, где стандарт недоговаривает про типы выражений. А определение типа выражения через что-то другое ничего не даст, кроме твоих вопросов "что есть это другое?".


Пример бы какой-нибудь. Что, например, подправить и как. Просто для лучшего понимания.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[37]: Зачем плюс?
От: N. I.  
Дата: 29.11.18 17:59
Оценка:
σ:

σ>Но вообще в этой инициазизации выражение попадает под 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 интерпретируется согласно правилу, которое ты уже цитировал: "For other objects, the interpretation of the values found therein is determined by the type of the expressions ([expr.compound]) used to access them" из http://eel.is/c++draft/intro.object#1

Только вот если через lvalue типа char мы пытаемся получить значение какого-нибудь полиморфного объекта, то де-юре это правило не работает, хотя де-факто должно
Отредактировано 29.11.2018 18:05 N. I. . Предыдущая версия .
Re[38]: Зачем плюс?
От: σ  
Дата: 29.11.18 18:23
Оценка:
NI>Но значение этого lvalue интерпретируется согласно правилу, которое ты уже цитировал: "For other objects, the interpretation of the values found therein is determined by the type of the expressions ([expr.compound]) used to access them" из http://eel.is/c++draft/intro.object#1

Я же сказал, что оно невнятное. Есть мнение, что это мёртвое правило, до которого просто не доходят руки. Что вообще такое "interpretation of the values"? Я бы понял "interpretation of the storage as storing an object of the expression's type"... Бессмысленное словосочетание, бессмысленное правило. Это дохлое правило с нами из 98-го стандарта, а пункты про значения указателей и то, на что указывает glvalue — свежие. По-моему, значения указателей определили для std::launder.

Кстати, приятным побочным эффектом новых правил является то, что теперь понятно, какое значение у выражения L_to_R(*reinterpret_cast<unsigned*>(&i)) — такое же, как у L_to_R(i). А вот как раньше можно было сказать, какое значение у первого выражения? Насколько я знаю, стандарт уклоняется от описания представления целых чисел и вообще любых объектов. И не требует этого от реализаций, за исключением представлений для char, указателей и чисел с плавающей точкой.

В общем, разрешение получать доступ к объекту через соответсвующий знаковый или беззнаковый тип перестало быть бесполезным.
А разрешение получать доступ к байтам объекта как было бесполезным, так и осталось, т.к. всё равно результат этого доступа не определён стандартом и не должен определяться реализацией. UB as is.
Re[38]: Зачем плюс?
От: σ  
Дата: 29.11.18 18:37
Оценка:
σ>>Нужно просто пофиксить места, где стандарт недоговаривает про типы выражений. А определение типа выражения через что-то другое ничего не даст, кроме твоих вопросов "что есть это другое?".

R>Пример бы какой-нибудь.


Тут пол-треда спрашивают про тип (n+1).
https://timsong-cpp.github.io/cppwp/n4659/expr#def:usual_arithmetic_conversions определяет type of the result, но не type of the expression. Раньше одно было слабо отделено от другого. Но с тех пор как определили (wg21.link/p0135r1) что такое result, это не так.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.