Почему в C++ 11 содержится странное предложение о сравнении пустых указателей?



c++11 pointers (1)

TL; DR:

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

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

  1. Сравнение реляционного указателя с константой нулевого указателя Раздел: 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: сравнение указателей и преобразований квалификации

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

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

Преобразования указателя (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 года.

И вот как это было включено в стандарт C ++ 11.

Проверяя ссылки на другой вопрос, я заметил странное предложение в 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 и сразу удаленном? Или, может быть, он все еще там, но перенесен / подразумевается какой-то другой частью стандарта?





comparison-operators