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 ++ говорится, что разыменование неинициализированного указателя является неопределенным поведением? делает этот аргумент, и я не могу примирить аргумент с тем, что изложено в любой из последних версий проекта стандарта. Так как я видел это несколько раз, я хотел получить разъяснения.
Фактическое доказательство неопределенного поведения не так важно, так как, как я отметил в связанном выше вопросе, у нас есть другой способ добраться до неопределенного поведения.
Я думаю, что вы подходите к этому с довольно косой точки зрения, так сказать. Согласно §5.3.1 / 1:
Одинарный
*
оператор выполняет косвенность: выражение, к которому оно применяется, должно быть указателем на
тип объекта или указатель на тип функции, и результатом является lvalue, ссылающееся на объект или функцию
на что указывает выражение. Если тип выражения «указатель на T», тип результата «T».
Хотя это не говорит о преобразовании lvalue в rvalue, оно требует, чтобы выражение было указателем на объект или функцию. Неинициализированный указатель не будет (за исключением, возможно, случайно) такой вещи, поэтому попытка разыменования дает неопределенное поведение.
Я преобразовал раздел обновления в своем вопросе в ответ, так как на данный момент это кажется ответом, хотя и неудовлетворительным, что мой вопрос не подлежит ответу:
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 гласит:
Это определение относится к указателям, поскольку указатели являются итераторами. Эффект разыменования итератора, который был признан недействительным, не определен.