Учитывая, что 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

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

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

1 ответ

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

Согласно пункту 2 пункта 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]. Это кажется слишком строгим для сравнения указателей, и текущие реализации принимают пример.

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

Это было решено в стандарте N3624: Основная проблема 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

[Примечание: учитывая одинаковые типы T1 и T2, эта конструкция гарантирует, что оба могут быть преобразованы в T3. - примечание конца] Тип составного указателя двух операндов 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". - конец примера]

Другие вопросы по тегам