Ответ 1
В С++ 14 этот код плохо сформирован, но до С++ 14 это был хорошо сформированный код (но результат неуточнен), поскольку отчет о дефектах 583: Сравнение реляционных указателей с константой нулевого указателя:
В C это плохо сформировано (cf C99 6.5.8):
void f(char* s) { if (s < 0) { } }
... но в С++ это не так. Зачем? Кому нужно писать (s > 0) когда они могли так же писать (s!= 0)?
Это было на языке с ARM (и, возможно, раньше); по-видимому, потому, что для преобразования указателей (4.10 [conv.ptr]) для выполнения обоих операндов, когда один из операндов тип указателя. Таким образом, он выглядит как "null-ptr-to-real-pointer-type" преобразование - это переход с другими преобразованиями указателей.
В С++ 14 это было сделано плохо сформированным, когда N3624 был применяется к черновому стандарту С++ 14, который является пересмотром N3478
. Предлагаемая резолюция к 583
отмечает:
Эта проблема решается решением проблемы 1512.
и выпуска 1512
предлагаемое разрешение N3478
(N3624 - это ревизия N3478):
Предлагаемая формулировка содержится в документе N3478.
Изменения в разделе 5.9 от С++ 11 до С++ 14
Раздел 5.9
Операторы отношения сильно изменились между стандартным проектом С++ 11 и С++ 14 черновик стандарта, следующие основные моменты выделяют наиболее существенные различия (акцент мой вперед), из параграфа 1
:
Операнды должны иметь арифметику, перечисление или тип указателя, или Тип std:: nullptr_t.
изменяется на:
Операнды должны иметь арифметику, перечисление или тип указателя
Таким образом, тип std:: nullptr_t больше не является допустимым операндом, но все еще оставляет 0
, который является константой нулевого указателя и поэтому можно преобразовать (раздел 4.10
) в тип указателя.
Это описано в параграфе 2
, которое в С++ 11 гласит:
[...] Преобразования указателей (4.10) и квалификационные преобразования (4.4) выполняются на операндах указателей (или на операнде указателя и нулевом значении константу указателя или две константы нулевого указателя, по меньшей мере один из который не является неотъемлемым), чтобы привести их к их составному типу указателя. Если один операнд является константой нулевого указателя, тип составного указателя is std:: nullptr_t, если другой операнд также является константой нулевого указателя или, если другой операнд является указателем, тип другого операнд. [...]
это явно предоставляет исключение для операнда константы нулевого указателя, в С++ 14 изменяется следующее:
Обычные арифметические преобразования выполняются на операндах арифметический или перечисляемый тип. Если оба операнда являются указателями, указатель конверсии (4.10) и квалификационные преобразования (4.4) чтобы привести их к их составному указателю (раздел 5). После преобразования, операнды должны иметь один и тот же тип.
В котором нет случая, позволяющего преобразовать 0
в тип указателя. Оба операнда должны быть указателями, чтобы применять преобразования указателей, и требуется, чтобы операнды имели один и тот же тип после конверсий. Это не выполняется в случае, когда один операнд является типом указателя, а другой - константой нулевого указателя 0
.
Что делать, если оба операнда являются указателями, а одно - значением нулевого указателя?
R Sahu спрашивает, правильно ли сформирован следующий код?:
char* p = "";
char* q = nullptr;
if ( p > q ) {}
Да, в С++ 14 этот код хорошо сформирован, оба p
и q
являются указателями, но результат сравнения не указан. Определенные сравнения для двух указателей указаны в параграфе 3
и говорят:
Сравнение указателей с объектами определяется следующим образом:
Если два указателя указывают на разные элементы одного и того же массива или на его субобъекты, указатель на элемент с более высоким индекс больше.
Если один указатель указывает на элемент массива или на его подобъект, а другой указатель указывает на один из последних элементов массив, последний указатель сравнивается больше.
Если два указателя указывают на разные нестатические элементы данных одного и того же объекта или на подобъекты таких элементов, рекурсивно, указатель на более поздний объявленный элемент сравнивается больше, если два члены имеют одинаковый контроль доступа (п. 11) и класс не является объединением.
Значения нулевых указателей здесь и далее не определены в параграфе 4
, в котором говорится:
[...] В противном случае результат каждого из операторов не указан.
В С++ 11 он специально делает результаты неопределенными в параграфе 3
:
Если два указателя p и q того же типа указывают на разные объекты которые не являются членами одного и того же объекта или элементов одного и того же массива или к различным функциям, или если только один из них является нулевым, результаты p < q, p > q, p <= q и p >= q не определены.