Подтвердить что ты не робот

Учитывая, что int ** p1 и const int ** p2 p1 == p2 хорошо сформированы?

Учитывая следующую функцию:

void g(int **p1, const int**p2)
{
   if (p1 == p2) { }  
}

clang (вернуться к версии 3.0) выдает это предупреждение (видеть его в прямом эфире):

warning: comparison of distinct pointer types ('int **' and 'const int **')
uses non-standard composite pointer type 'const int *const *' 
[-Wcompare-distinct-pointer-types]
  if (p1 == p2) { }
      ~~ ^ ~~

Использование флагов -pedantic-errors превращает его в ошибку. Ни gcc (вернуться к 4.3.6), ни Visual Studio (2013) не выдают предупреждение, в соответствии со стандартом, это сравнение:

p1 == p2

хорошо сформировано?

В более общем плане, если два многоуровневых указателя отличаются друг от друга по сравнению с первым уровнем, сравнение осуществляется через оператор равенства или корректные отношения реляционных операторов?

4b9b3361

Ответ 1

До С++ 14 этот случай был плохо сформирован, и более общий случай с некоторыми исключениями также был плохо сформирован. Это описано в отчет о дефекте 1512: сравнение указателей и конверсии квалификации, в котором говорится:

В соответствии с пунктом 5.9 [expr.rel], описывающим указатель сравнения,

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

Это, как представляется, делает следующий пример плохо сформированным,

bool foo(int** x, const int** y) {
   return x < y;  // valid ?
}

потому что int ** не может быть преобразован в const int **, в соответствии с правила 4.4 [conv.qual], пункт 4. Это кажется слишком строгим для сравнение указателей и текущие реализации принимают пример.

В отчете о дефектах указывается, хотя это было плохо сформировано, реализации приняли такие сравнения. Этот clang commit указывает, что он рассматривался как расширение и указывает, что оба gcc и EDG также рассматривают это как расширение, по-видимому, это также случай для Visual Studio.

Это было разрешено в стандарте N3624: Core Issue 1512: Сравнение указателей и конверсий квалификации, в котором говорится:

В настоящем документе представлены необходимые изменения рабочего проекта для решения основных проблем 583 и 1512. В частности, он делает

[...]

и

void g(int **p1, const int**p2)
{
   if (p1 == p2) { ... }
}

хорошо сформированы.

Также обратите внимание, что в встреча была принята, было отмечено, что эта только что кодифицированная существующая практика.

Среди других изменений в стандарте этот абзац был добавлен в конец раздела 5 [expr], который включает новый термин cv-комбинированный тип:

Cv-комбинированный тип двух типов T1 и T2 является типом T3, подобным T1 чья cv-квалификационная сигнатура (4.4) равна:

  • для каждого j > 0, cv3, j является объединением cv1, j и cv2, j;
  • если полученный cv3, j отличается от cv1, j или cv2, j, то const добавляется к каждому cv3, k для 0 < k < к.

[Примечание: для аналогичных типов T1 и T2, эта конструкция гарантирует, что обе они могут быть преобразованы в T3. -end note] Тип составного указателя двух операндов p1 и p2 имеющие типы T1 и T2 соответственно, где по меньшей мере один является указателем или указатель на тип члена или std:: nullptr_t:

  • если оба p1 и p2 являются константами нулевого указателя, std:: nullptr_t;
  • если p1 или p2 - константа нулевого указателя, T2 или T1 соответственно;
  • если T1 или T2 является "указателем на cv1 void", а другим типом является "указатель на cv2 T", "указатель на cv12 void", где cv12 является объединением cv1 и cv2;
  • если T1 является "указателем на cv1 C1", а T2 является "указателем на cv2 C2", где C1 ссылается на C2 или C2, ссылается на C1 (8.5.3), cv-комбинированный тип T1 и T2 или cv-комбинированный тип T2 и T1, соответственно;
  • если T1 является "указателем на член C1 типа cv1 U1", а T2 является "указателем на элемент C2 типа cv2 U2", где C1 является ссылкой, связанным с C2 или C2 является ссылкой, связанной с C1 (8.5.3), cv-комбинированным типом T2 и T1 или cv-комбинированный тип T1 и T2 соответственно;
  • если T1 и T2 похожи на многоуровневый смешанный указатель и указатель на типы членов (4.4), cv-комбинированный тип T1 и T2;
  • В противном случае программа, которая требует определения типа составного указателя, плохо сформирована.

[Пример:

    typedef void *p;
    typedef const int *q;
    typedef int **pi;
    typedef const int **pci;

Тип составного указателя p и q является "указателем на const void"; Тип составного указателя pi и pci - это "указатель на const-указатель на const int". -end example]