Как сравнить две стандартные последовательности преобразования, используя ранг содержащихся преобразований

#c #language-lawyer

#c #язык-юрист

Вопрос:

 #include <iostream>
void g(int*);  //#1
void g(int (amp;arr)[2]);  //#2

void f(int*);  //#3
void f(int const*);  //#4
int main(){
  int arr[2] ={0};
  f(arr);    // choose #3
  g(arr);  //ambiguous
}
 

Рассмотрим приведенный выше код, для которого выбран #3 f(ptr) , однако g(arr) он дает ambiguous диагностику.

Правило выбора наилучшей функции определяется следующим образом:

Стандартная последовательность преобразования S1 является лучшей последовательностью преобразования, чем стандартная последовательность преобразования S2, если

  • S1 является правильной подпоследовательностью S2 (сравнение последовательностей преобразования в канонической форме, определенной [over.ics.scs], исключая любое преобразование Lvalue; последовательность преобразования идентичности считается подпоследовательностью любой неидентичной последовательности преобразования) или, если нет, что

Итак, взгляните на over.ics.scs # 3

Они используются для ранжирования стандартных последовательностей преобразования. Ранг последовательности преобразования определяется с учетом ранга каждого преобразования в последовательности и ранга любой ссылочной привязки.

Согласно моему пониманию приведенного выше правила, я могу понять, почему #3 это лучшая перегрузка для f(ptr) , то есть:

Задается S1 как (arr => int *):

 Array-to-pointer conversion -> (identity conversion)  
^^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^                   
     int[2] => int*             int* => int* 
 

при заданном значении S2 как (ptr => int const *)

 Array-to-pointer conversion -> Qualification conversions ->  identity conversion   
^^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^     ^^^^^^^^^^^^^^^^^^^ 
     int[2] => int*               int* => int const*           int const* => int const* 
 

Поскольку identity conversion является правильной подпоследовательностью Qualification conversions , следовательно, S1 лучше, чем S2. Итак, #3 выбирается по разрешению перегрузки для f(ptr) .

Когда я использую аналогичный процесс для определения того, что лучше g(arr) , я сталкиваюсь с проблемой.

Опять же, учитывая S1 как (arr => int *)

 Array-to-pointer conversion -> identity conversion  
^^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^ 
      int[2] => int*              int* => int*
 

при заданном S2 как(arr => int (amp;arr)[2])

Когда параметр ссылочного типа привязывается непосредственно к выражению аргумента, неявная последовательность преобразования является преобразованием идентификатора, если только выражение аргумента не имеет тип, который является производным классом типа параметра, и в этом случае неявная последовательность преобразования является преобразованием производного в базовое

 identity conversion
^^^^^^^^^^^^^^^^^^^
  bind to reference   
 

Здесь identity conversion of S2 является правильной подпоследовательностью Array-to-pointer conversion of S1 , следовательно, это должно быть лучше, чем S1 , почему компилятор жаловался g(arr) на неоднозначный вызов?

Есть ли у меня какие-либо неправильные представления о том, как ранжировать стандартные последовательности преобразования? Как сравнить два стандартных ICS (ранг содержащегося преобразования)?

Комментарии:

1. В целевом примере используются шаблоны функций, но к созданным функциям применяются те же правила ранжирования ics.

2. @cigien Пример не интерпретирует этот вопрос. Хотя все они имеют точный ранг, однако преобразование идентификаторов лучше, чем любое другое преобразование, имеющее точный ранг. В противном случае, почему f(ptr) не является двусмысленным, не qualification conversion имеет точного ранга ?

3. Чтобы уточнить, вы спрашиваете, почему вызов g(arr); неоднозначен, не так ли? Или вы ищете ответ, который конкретно учитывает ваше понимание правил?

4. @cigien оба. На мой взгляд, каждое преобразование в f(arr); g(arr); целом содержит только преобразование с точным рангом , но почему g(arr); — неясно. Мне нужна интерпретация этих вопросов.

5. Ах, хорошо. Вероятно, есть цель, почему f(arr) не является двусмысленной, но я все равно вернусь к этому вопросу.

Ответ №1:

Ключевой момент заключается вот в чем:

S1 является правильной подпоследовательностью S2 (сравнение последовательностей преобразования в канонической форме, определенной [over.ics.scs], исключая любое преобразование Lvalue; последовательность преобразования идентичности считается подпоследовательностью любой неидентичной последовательности преобразования) или, если нет, что

Это означает, что для вызова функции g(arr) все преобразования массива в указатель не используются для определения ранга. Другими словами, от типа int[2] к типу int* существует только преобразование идентификаторов, которое использовалось для определения ранга. Следовательно, S1 of void g(int*); и S2 of void g(int (amp;arr)[2]); являются неразличимыми микросхемами, поэтому компилятор выдает неоднозначную ошибку.

В отличие от этого, преобразования для void f(int*); и void f(int const*); , используемые для сравнения ранга, являются identity conversion и qualification conversion , соответственно.

В соответствии с правилом:

последовательность преобразования идентификаторов считается подпоследовательностью любой последовательности преобразования, не являющейся идентичной

Следовательно, Qualification conversion считается, что имеет худший ранг, чем у identity conversion . Итак, void f(int*) выиграл соревнование.

Комментарии:

1. Нет, преобразование квалификации , которое находится в категории корректировки квалификации , имеет тот же ранг, что и Удостоверение личности . Оба они оцениваются как Точное совпадение .

Ответ №2:

Вы пытаетесь применить over.ics.rank-3.2.1 к этим наборам перегрузок, но это правило не применяется ни f к одному, ни g к другому.


Учитывая вызов f(arr); , при выполнении разрешения перегрузки для f обеих перегрузок требуется стандартная последовательность преобразования, состоящая из преобразования массива в указатель, и оба имеют одинаковый ранг, что является точным совпадением. Тай-брейк, используемый в этом случае, — over.match.best#over.ics.rank-3.2.5:

Стандартная последовательность преобразования S1 является лучшей последовательностью преобразования, чем стандартная последовательность преобразования S2, если

S1 и S2 отличаются только своим квалификационным преобразованием ([conv.qual] ) и дают аналогичные типы T1 и T2 соответственно, где T1 может быть преобразован в T2 с помощью квалификационного преобразования.

После этого правила приведен пример, демонстрирующий, как работает правило.

Для набора перегрузки f T1 есть int * и T2 есть int const * , и T1 может быть преобразован в T2 путем преобразования квалификации.


В случае вызова g(arr); при выполнении разрешения перегрузки g(int (amp;)[2]) перегрузка оценивается как точное совпадение, поскольку требуемая стандартная последовательность преобразования не требуется.

Однако перегрузка g(int*) также оценивается как точное совпадение, поскольку стандартная необходимая последовательность преобразования — это преобразование массива в указатель.

f Однако, в отличие от for , в [over.ics.rank] нет правила, которое устраняет неоднозначность между стандартными последовательностями преобразования for g , и вызов завершается с ошибкой.

Комментарии:

1. Не могли бы вы сказать мне, к какому случаю относится «S1 — правильная подпоследовательность»?

2. @jackX Возможно, но мне придется немного подумать об этом. Несмотря на это, это был бы отдельный вопрос, который я действительно не хочу обсуждать в комментариях.

3. Вернемся к этому вопросу: «S1 и S2 отличаются только своим квалификационным преобразованием», однако S1 не содержит qualification conversion , вместо этого S2 содержит a qualification conversion . В приведенном правиле, похоже, говорится S1 , S2 что оба содержат qualification conversion разные s.

4. посмотрите на этот случай qualification conversion (устраните неопределенность того, является ли преобразование идентификаторов квалификационным преобразованием), который больше не может предоставлять привилегии для этого случая ( преобразование указателя функции абсолютно точно соответствует рангу). Вот преобразование идентификаторов по сравнению с преобразованием указателя функции . IMO, over.ics.rank #3.2.1 применяется здесь.

5. » В приведенном правиле, похоже, говорится, что S1 и S2 содержат разные квалификационные преобразования». Не совсем, просто одно может быть преобразовано в другое. Кроме того, я не уверен, что ваша демонстрация применима: noexcept насколько мне известно, спецификаторы нельзя добавлять в качестве квалификационного преобразования .