Следующий ошибочный код для проверки нулевого указателя компилируется некоторыми компиляторами, но не другими (см. godbolt):
bool f()
{
char c;
return &c > nullptr;
}
Наступательная часть — это относительное сравнение между указателем и nullptr.
Сравнение компилируется с
Но никакая версия 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 не является, если я правильно прочитал стандарт, интегральным типом.
@IgorTandetnik Но его можно преобразовать в тип указателя, как одно из стандартных преобразований. 7.3.12: «Константа нулевого указателя может быть преобразована в тип указателя; результатом является значение нулевого указателя этого типа».
Да. Но ничто в [expr.rel] не говорит о том, что если один операнд является указателем, то другой преобразуется в указатель того же типа. В этом разделе упоминаются стандартные преобразования lvalue-to-rvalue, массива в указатель, функции в указатель и обычные арифметические преобразования. Не преобразования указателя.
Возможный сопутствующий вопрос: void *p...; если (p > 0) .... Это неопределенное поведение?
@Игорь Понятно. Я прочитал только «стандартные преобразования», но в абзаце выбрано несколько конкретных. ХОРОШО...
@IgorTandetnik И для сравнения на равенство разрешенные преобразования действительно включают преобразования указателя: «Если хотя бы один из операндов является указателем, преобразования указателя (7.3.12) [...] выполняются для обоих операндов».





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