Требует ли стандарт преобразования lvalue в rvalue переменной-указателя при применении косвенного обращения?

TL; DR

Учитывая следующий код:

int* ptr;
*ptr = 0;

делает *ptr требовать Lvalue к Rvalue преобразование ptr перед применением косвенного обращения?

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

подробности

Lvalue к Rvalue преобразование покрыто N3485 в разделе 4.1 Преобразование Lvalue в Rvalue параграф 1 и говорит (Акцент шахты идет вперед):

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

Так же *ptr = 0; необходимо это преобразование?

Если мы идем в раздел 4 параграф 1 это говорит:

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

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

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

и параграф 11 который говорит:

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

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

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

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

Почему мы заботимся?

Я видел ответ и комментарии в других вопросах, в которых утверждается, что использование неинициализированного указателя является неопределенным поведением из-за необходимости Lvalue к Rvalue преобразование ptr перед применением косвенного Например: Где именно в стандарте C ++ говорится, что разыменование неинициализированного указателя является неопределенным поведением? делает этот аргумент, и я не могу примирить аргумент с тем, что изложено в любой из последних версий проекта стандарта. Так как я видел это несколько раз, я хотел получить разъяснения.

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

19

Решение

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

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

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

13

Другие решения

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

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

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

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

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

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

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

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

а также:

Недопустимый итератор — это итератор, который может быть единственным.268

и сноска 268 гласит:

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

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

4

По вопросам рекламы [email protected]