prezident.mira:
PM>232 — это исключительно про wording, а не про UB или не UB.
Формулировки как раз и определяют поведение.
NI>>Ещё раз: что будет результатом выражения *p, где p — нулевой указатель?
PM>Что будет результатом — описано в пункте, который тут уже многократно цитировался и я не поленюсь сделать это ещё раз:
PM>PM>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
Не будет там никакого lvalue referring to the object, просто в силу очевидной невозможности.
NI>>"Не пойми что"?
PM>"Не пойми что" там только из-за bad wording.
C составлением good wording, которое бы всех устраивало, дела пока как-то не очень, issue 232 висит уже заметно больше 10 лет. При этом стандартизаторам, наверное, уже 100500 раз задавали вопросы на этот счёт (и порядком ими задолбали), что, по идее, должно бы дать хороший стимул разрешить эту проблему. Однако признаков, что это наконец-то случится в ближайшем будущем, что-то пока не видать, а значит, не всё так просто.
NI>>И что я дальше могу делать с этим "не пойми что"?
PM>Дальше Вы можете, например, применить оператор взятия адреса.
И что я получу?
[expr.unary.op]/3:
Otherwise, if the type of the expression is T, the result has type “pointer to T” and is a prvalue that is the address of the designated object (1.7) or a pointer to the designated function.
Если lvalue не ссылается ни на объект, ни на функцию, то и адрес брать не у кого. Примерно такое же неявное undefined behavior, что и при разыменовании. Есть и другой похожий способ получения undefined behavior:
int &f()
{
int local;
return local;
}
int main()
{
&f(); // undefined behavior
}
PM>И то, я подозреваю, что indirection throug означает использование его для доступа к значению, т.е. lvalue-to-rvalue conversion, а не просто применение оператора indirection.
Нет, это именно применение оператора разыменования, иначе там бы говорилось об accessing.
PM>Да. Поведение определено для всех значений, кроме тех, про которые явно не сказано, что оно не определено. Так же, как, например, со сложением int: не нужно перечислять все пары значений и их результат. Без этого понятно, что определено для всех. Кроме тех случаев, которые вызывают переполнение.
Хорошо, давай попробуем вычислить вот такое сложение: *(int *)nullptr + 1. Какое поведение тут будет?
PM>А пункт про "UB, если стандарт не определяет поведение" применяется примерно так:
PM>PM>Поведение описано для применения оператора * к выражению, которое имеет тип "указатель на объект (или функцию)". Что происходит при применении к выражению с другим типом? Это не определено, а, значит, по пункту про UB это UB.
Согласно [intro.compliance], требование "the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type" относится к diagnosable semantic rules, и при его нарушении "a conforming implementation shall issue at least one diagnostic message" (это не undefined behavior).
PM>>>Требования к входному выражению предъявлены, результат описан.
По факту результат описан лишь для некоторых возможных случаев, но не для всех. Если описанный результат заведомо неверный, то ни о какой определённости речи быть не может.
NI>>Результат, которого там на самом деле не может быть? И что я могу делать с этим результатом? И какие правила при этом должен применять? Например, я захотел использовать *(int volatile *)nullptr в качестве discarded-value-expression. В какие пункты стандарта мне смотреть, чтоб выяснить, как оно себя может повести?
PM>На первый ряд вопросов я ответил выше, а про volatile Вы сами писали.
Вот только я не сказал, каким именно образом lvalue-to-rvalue conversion, будучи применённым к такому lvalue, порождает undefined behavior. И я бы с большим интересом посмотрел на выкладки, опирающиеся на явное указание того, что здесь имеет место undefined behavior (именно в соответствии с текстом стандарта, без правок, не имеющих официальный статус).