Почему в C ++ 11 содержится странное предложение о сравнении пустых указателей? - PullRequest
0 голосов
/ 25 января 2019

Проверяя ссылки на другой вопрос, я заметил странное предложение в C ++ 11, в [expr.rel] ¶3:

Указатели на void (после преобразования указателей) можно сравнить с результатом, определяемым следующим образом: Если оба указатели представляют один и тот же адрес или оба являются нулевым значением указателя, результат равен true, если оператор <= или >= и false в противном случае; в противном случае результат не указан.

Это, кажется, означает, что, как только два указателя были приведены к void *, их отношение порядка больше не гарантируется; например, это:

int foo[] = {1, 2, 3, 4, 5};
void *a = &foo[0];
void *b = &foo[1];
std::cout<<(a < b);

может показаться неуказанным.

Интересно, что этот пункт отсутствовал в C ++ 03 и исчез в C ++ 14, поэтому, если мы возьмем приведенный выше пример и применим к нему формулировку C ++ 14, я бы сказал, что ¶3.1

  • Если два указателя указывают на разные элементы одного и того же массива или его подобъектов, указатель на элемент с более высоким индексом сравнивается больше.

будет применяться, так как a и b указывают на элементы одного и того же массива, даже если они были преобразованы в void *. Обратите внимание, что формулировка ¶3.1 была примерно такой же в C ++ 11, но, казалось, была переопределена предложением void *.

Прав ли я в своем понимании? Какой смысл в этом странном предложении, добавленном в C ++ 11 и сразу удаленном? Или, может быть, он все еще там, но перенесен / подразумевается какой-то другой частью стандарта?

1 Ответ

0 голосов
/ 25 января 2019

TL; DR:

  • в C ++ 98/03 предложение отсутствует, а стандарт не определяет реляционные операторы для void указателей (ядровыпуск 879, см. конец этого поста);
  • странное предложение о сравнении void указателей было добавлено в C ++ 11 для его решения, но это, в свою очередь, привело к двум другим основным проблемам 583 и 1512(см. ниже);
  • решение этих проблем потребовало удаления этого пункта и замены на формулировку, найденную в стандарте C ++ 14, которая допускает «нормальное» void * сравнение.

Основная проблема 583: Сравнение реляционного указателя с константой нулевого указателя

Сравнение реляционного указателя с константой нулевого указателя Раздел: 8,9 [expr.rel]

В C это плохо сформировано (см. C99 6.5.8):

void f(char* s) {
    if (s < 0) { }
} ...but in C++, it's not. Why? Who would ever need to write (s > 0) when they could just as well write (s != 0)?

Это было в языке начиная с ARM (и, возможно, раньше);очевидно, это потому, что преобразования указателей (7.11 [conv.ptr]) необходимо выполнять для обоих операндов, когда один из операндов имеет тип указателя.Таким образом, похоже, что преобразование «null-ptr-to-real-pointer-type» объединяет другие преобразования указателей.

Предлагаемое разрешение (апрель 2013 г.):

Этопроблема решена путем разрешения проблема 1512 .

основная проблема 1512: Сравнение указателей и квалификационных преобразований

Сравнение указателей и преобразований квалификаций Раздел: 8.9 [expr.rel]

Согласно параграфу 2 8.9 [expr.rel], описывающему сравнения указателей,

Преобразования указателей (7.11[conv.ptr]) и квалификационные преобразования (7.5 [conv.qual]) выполняются над операндами указателя (или над операндом указателя и константой нулевого указателя, или над двумя константами нулевого указателя, по крайней мере, одна из которых не является целочисленной) привести их к их составному типу указателя.Это может привести к тому, что следующий пример будет некорректным,

 bool foo(int** x, const int** y) {
 return x < y;  // valid ?   } because int** cannot be converted to const int**, according to the rules of 7.5 [conv.qual] paragraph 4.

Это кажется слишком строгим для сравнения указателей, и текущие реализации принимают этот пример.

Предлагаемое решение (ноябрь 2012 г.):


Соответствующие выдержки из решения вышеуказанных проблем приведены в документе: Сравнение указателей и квалификационных преобразований (редакция 3) .

Следующее также решает основной выпуск 583 .

Изменение в 5.9 expr.rel параграфы с 1 по 5:

В этом разделе следующее утверждение (нечетное предложение в C ++ 11 ) было удалено :

Указатели на void (после преобразования указателей) можно сравнить с результатом, определяемым следующим образом: Если оба указателя представляют один и тот же адрес или оба являются нулевыми значениями указателя, результатом будет true, если оператор равен <= или>= и false в противном случае;в противном случае результат не определен

И следующие операторы были добавлены :

  • Если два указателя указывают на разные элементы одного и того жеВ массиве или его подобъектах указатель на элемент с более высоким индексом сравнивается больше.
  • Если один указатель указывает на элемент массива или его подобъекта, а другой указатель указывает на один последний элемент массива, последний указатель сравнивается больше.

Таким образом, в окончательном рабочем проекте раздела C ++ 14 (n4140) [expr.rel] / 3 приведенные выше утверждения найдены в том виде, в котором они были заявленына момент принятия решения.


Копание по причине добавления этого странного предложения привело меня к гораздо более ранней проблеме 879: Отсутствие встроенных операторов сравнения для типов указателей ,Предложенное решение этого вопроса (в июле 2009 года) привело к добавлению этого пункта, который был утвержден WP в октябре 2009 года.стандарт.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...