Является ли стандартный мандат преобразованием переменной lvalue-rval переменной указателя при применении косвенности?

TL; DR

С учетом следующего кода:

int* ptr;
*ptr = 0;

делает *ptr требуется преобразование значения lvalue-r-значение ptr перед применением косвенности?

Стандарт охватывает тему lvalue-to-rvalue во многих местах, но, похоже, не указывает достаточно информации, чтобы определить, требует ли оператор * такого преобразования.

Подробнее

Преобразование lvalue-to-rval покрывается в N3485 в разделе 4.1 Преобразование 1-го значения в значение 1 и 1 говорит (акцент мой идет вперед):

Значение gl (3.10) нефункционного типа без массива T может быть преобразовано к prvalue.53 Если T - неполный тип, программа, которая требует, чтобы это преобразование плохо сформировалось. Если объект, которому glvalue не является объектом типа T и не является объектом тип, полученный из T, или если объект не инициализирован, программа что требует такого преобразования undefined поведение. [...]

Значит, *ptr = 0; требует этого преобразования?

Если мы перейдем к пункту 1 раздела 4, он говорит:

[...] Стандартная последовательность преобразования будет применена к выражению при необходимости, чтобы преобразовать его в требуемый тип адресата.

Итак, когда это необходимо? Если мы рассмотрим раздел 5 Выражения, преобразование lvalue-to-rvalue упоминается в пункте 9, в котором говорится:

Всякий раз, когда выражение glvalue появляется как операнд оператора который ожидает prvalue для этого операнда, lvalue-to-rvalue (4.1), стандарт "массив-к-указателю" (4.2) или стандартная функция-указатель (4.3) конверсии применяются для преобразования выражения в prvalue. [...]

и параграф 11, в котором говорится:

В некоторых контекстах выражение появляется только для его побочных эффектов. Такое выражение называется выражением отбрасываемого значения. [...] Преобразование lvalue-to-rvalue (4.1) применяется тогда и только тогда, когда выражение является lvalue волатильно-квалифицированного типа и является одним из следующие [...]

ни один из абзацев не применим к этому образцу кода и 5.3.1 Унарные операторы, указанные в пункте 1, говорят:

Унарный * оператор выполняет косвенное направление: выражение, которому оно применяется указатель на тип объекта или указатель на тип функции, и результатом является lvalue, относящееся к объекту или функции, к которой относится выражение. Если тип выражения является "указателем на T", тип результата - "T". [Примечание: косвенность через указатель на неполный тип (кроме cv void). Полученное таким образом lval может использоваться ограниченным образом (для инициализации ссылка, например); это значение не должно быть преобразовано в prvalue, см. 4.1. -end note]

похоже, не требуется значение указателя, и я не вижу никаких требований к преобразованию указателя здесь, я что-то упускаю?

Почему нам все равно?

Я видел ответ и комментарии в других вопросах, в которых утверждается, что использование неинициализированного указателя - это поведение undefined из-за необходимости преобразования lvalue-to-rval ptr перед применением косвенности. Например: Где, на самом деле, стандарт С++ говорит о разыменовании неинициализированного указателя, это undefined поведение? делает этот аргумент, и я не могу примирить аргумент с тем, что выложено в любом из последних проектов вариантов стандарта. Поскольку я видел это несколько раз, я хотел получить разъяснения.

Фактическое доказательство поведения undefined не так важно, поскольку, как я заметил в связанном вопросе выше, мы имеем другой способ добраться до поведения undefined.

Ответы

Ответ 1

Я преобразовал раздел обновления в свой вопрос в ответ, так как на данный момент это, кажется, ответ, хотя и неудовлетворительный, что мой вопрос неопровержимый:

dyp указал мне на два соответствующих потока, которые покрывают очень похожую почву:

Похоже, что консенсус плохо указан и поэтому не может дать ответ, который я ищу, Джозеф Мэнсфилд разместил отчет о дефекте по этому отсутствию спецификации, и похоже, что он по-прежнему open и неясно, когда это может быть выяснено.

Есть несколько аргументов здравого смысла в отношении намерения стандарта. Можно аргументировать Логически, операнд является prvalue, если операция требует использования значения этого операнда. Другим аргументом является то, что если мы оглянемся на проект стандарта C99, то преобразование lvalue в rvalue по умолчанию, и отмечены исключения. Соответствующим разделом из проекта стандарта C99 является 6.3.2.1 Lvalues, массивы и обозначения функций, параграф 2, который гласит:

За исключением случаев, когда это операнд оператора sizeof, унарный и оператор, оператор ++, оператор-оператор или левый операнд. оператора или оператора присваивания, значение lvalue, которое не имеет типа массива, преобразуется в значение, хранящееся в указанном объекте (и больше не является lvalue). [...]

который в основном говорит с некоторыми исключениями, операнд преобразуется в значение, хранящееся, и поскольку косвенность не является исключением, если это поясняется также и в С++, то это действительно дало бы ответ на мой вопрос да.

Поскольку я попытался прояснить доказательство поведения undefined, было менее важно, чем уточнить, требуется ли преобразование lvalue-rvalue. Если мы хотим доказать поведение undefined, мы имеем альтернативные подходы. Подход Jerrys является здравым смыслом, и в этой косвенности требуется, чтобы выражение было указателем на объект или функцию, а неопределенное значение только случайно указывало бы на действительный объект. В общем случае проект стандарта С++ не дает явного выражения, чтобы сказать, что использование неопределенного значения - undefined, в отличие от стандартного проекта C99. В С++ 11 и обратно стандарт не дает явного выражения сказать, используя неопределенное значение undefined. Исключение составляют итераторы и указатели расширения, у нас есть понятие сингулярного значения, и в разделе 24.2.1 сказано:

[...] [Пример: после объявления неинициализированного указателя x (как и для int * x;), x всегда следует предполагать, что оно имеет сингулярное значение указателя. -end example] [...] Вызываемые значения всегда неособые.

и

Недействительный итератор является итератором, который может быть сингулярным. 268

и в сноске 268 говорится:

Это определение применяется к указателям, поскольку указатели являются итераторами. Эффект разыменования итератора, который был аннулирован, составляет undefined.

В С++ 1y язык изменяется, и у нас есть явный оператор, делающий использование промежуточного значения undefined с некоторыми узкими исключениями.

Ответ 2

Я думаю, вы приближаетесь к этому с довольно наклонного угла, если можно так выразиться. Согласно п. 5.3.1/1:

Унарный оператор * выполняет косвенное обращение: выражение, к которому оно применяется, должно быть указателем на тип объекта или указатель на тип функции, а результатом является lvalue, относящееся к объекту или функции на которое указывает выражение. Если тип выражения является "указателем на T", то тип результата будет "T."

Хотя это не говорит о преобразовании lvalue-to-rvalue, это требует, чтобы выражение было указателем на объект или функцию. Неинициализированный указатель не будет (за исключением, может быть, случайно), любой такой вещи, поэтому попытка разыменования дает поведение undefined.