P > nullptr: Неопределенное поведение?

Следующий ошибочный код для проверки нулевого указателя компилируется некоторыми компиляторами, но не другими (см. godbolt):

bool f()
{
    char c;
    return &c > nullptr;
}

Наступательная часть — это относительное сравнение между указателем и nullptr.

Сравнение компилируется с

  • gcc до 11.1
  • MSVC 19.latest с /std:c++17, но не с /std:c++20.

Но никакая версия clang (я проверил до 4.0) не компилирует его.

Ошибка более новых gccs ("упорядоченное сравнение указателя с целым нулем ('char*' и 'std::nullptr_t')"1 немного отличается от ошибки от clang ("недопустимые операнды в двоичном выражении ('char *' и 'std::nullptr_t')").

Стандарт C++20 ISO говорит об операторах отношения, применяемых к указателям в 7.6.9/3ff:

Если оба операнда являются указателями, преобразования указателя (7.3.12) [...] выполняются для привести их к составному типу указателя (7.2.2). После преобразования операнды должны иметь один и тот же тип.

Результат сравнения неравных указателей на объекты определяется в терминах частичного порядка, соответствующего следующим правилам:

(4.1) — Если два указателя указывают на разные элементы одного и того же массива или на его подобъекты, для сравнения требуется указатель на элемент с более высоким индексом. (4.2) — Если два указателя указывают на разные нестатические элементы данных одного и того же объекта или на подобъекты таких элементов, рекурсивно, указатель на более поздний объявленный элемент требуется для сравнения больше при условии, что два элемента имеют одинаковый контроль доступа. (11.9), ни один член не является подобъектом нулевого размера, и их класс не является объединением. (4.3) — В противном случае ни один указатель не требуется для сравнения больше, чем другой.

(«Указатель на объект» в этом контексте означает только то, что тип не является указателем на функцию, а не то, что значение указателя относится к реальному объекту.)

(4.1) и (4.2) здесь явно неприменимы, что оставляет (4.3). Означает ли (4.3), что «указатель не требуется», означает, что поведение не определено, а код недействителен? Для сравнения, стандарт 2012 года содержал формулировку в 5.9/2 "[...], если только один из [двух указателей одного типа p и q] равен нулю, результаты p<q, p>q, p<=q и p>=q не указаны».


1 Формулировка не верна. nullptr_t не является, если я правильно прочитал стандарт, интегральным типом.

nullptr является не указателем.
Igor Tandetnik 28.11.2022 00:37

@IgorTandetnik Но его можно преобразовать в тип указателя, как одно из стандартных преобразований. 7.3.12: «Константа нулевого указателя может быть преобразована в тип указателя; результатом является значение нулевого указателя этого типа».

Peter - Reinstate Monica 28.11.2022 00:41

Да. Но ничто в [expr.rel] не говорит о том, что если один операнд является указателем, то другой преобразуется в указатель того же типа. В этом разделе упоминаются стандартные преобразования lvalue-to-rvalue, массива в указатель, функции в указатель и обычные арифметические преобразования. Не преобразования указателя.

Igor Tandetnik 28.11.2022 00:45

Возможный сопутствующий вопрос: void *p...; если (p > 0) .... Это неопределенное поведение?

Ranoiaetep 28.11.2022 00:46

@Игорь Понятно. Я прочитал только «стандартные преобразования», но в абзаце выбрано несколько конкретных. ХОРОШО...

Peter - Reinstate Monica 28.11.2022 00:48

@IgorTandetnik И для сравнения на равенство разрешенные преобразования действительно включают преобразования указателя: «Если хотя бы один из операндов является указателем, преобразования указателя (7.3.12) [...] выполняются для обоих операндов».

Peter - Reinstate Monica 28.11.2022 00:51
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
111
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

7.6.9 говорится: «Выполняются стандартные преобразования lvalue-to-rvalue ([conv.lval]), массива в указатель ([conv.array]) и функции в указатель ([conv.func]). на операнды.... Преобразованные операнды должны иметь арифметический, нумерационный или указательный тип».

Ни одно из указанных преобразований не применимо к литералам nullptr. Кроме того, он не имеет арифметического типа, перечисления или указателя. Поэтому сравнение некорректно.

Другие вопросы по теме