Как определяется двусмысленность в алгоритме разрешения перегрузки?

Я пытаюсь понять метод разрешения перегрузки.

Почему это неоднозначно:

void func(double, int, int, double) {}
void func(int, double, double, double) {}

void main()
{
    func(1, 2, 3, 4);
}

но это не?

void func(int, int, int, double) {}
void func(int, double, double, double) {}

void main()
{
    func(1, 2, 3, 4);
}

В первом случае есть 2 совпадения точных параметров и 2 конверсии против 1 точного соответствия и 3 конверсии, а во втором случае - 3 точных совпадения и 1 конверсия против 1 точных совпадений и 3 конверсии.

Итак, почему одно двусмысленное, а другое нет? Какая здесь логика?

Ответ 1

Правила разрешения перегрузки определяют только частичный порядок в наборе всех совпадений - если перегрузка F1 не лучше, чем F2, это не означает, что F2 лучше соответствует, чем F1. Точный частичный порядок можно рассматривать как сравнение двух точек в измерениях k, где количество аргументов k. Давайте определим этот частичный порядок в точках в пространстве k -dim - (x_1, x_2,..., x_k) < (y_1, y_2,..., y_k) if x_i <= y_i for all i and x_j < y_j for at least one j. Это точно частичный порядок для кандидатов, не являющихся шаблонами, определенных стандартом.

Давайте рассмотрим ваши примеры:

void func(double, int,    int,    double) {}
                  vvv     vvv       vvv
                 better  better    equal
void func(int,    double, double, double) {}
          vvv                       vvv
         better                    equal

Таким образом, ни одна перегрузка не лучше, чем другая.

В вашем втором примере:

void func(int,   int,   int,   double) {}
          vvv    vvv    vvv     vvv
         equal  better better  equal
void func(int, double, double, double) {}
          vvv
         equal

Теперь первая перегрузка лучше второй во всех, кроме одного аргумента И никогда не хуже второй. Таким образом, нет никакой двусмысленности - частичный порядок действительно объявляет первое лучше.

(В приведенном выше описании не рассматриваются шаблоны функций. Более подробную информацию вы можете найти в cppreference.)

Ответ 2

Формулировка стандарта (§ [over.match.best]/1):

[...] пусть ICSi (F) обозначает неявную последовательность преобразований, которая преобразует i-й аргумент в списке в тип i-го параметра жизнеспособной функции F.
[...] жизнеспособная функция F1 определена как лучшая функция, чем другая жизнеспособная функция F2, если для всех аргументов я ICSi (F1) не является худшей последовательностью преобразования, чем ICSi (F2), а затем - для некоторого аргумента j ICSj (F1) является лучшей последовательностью преобразования, чем ICSj (F2)

В первом случае две функции не пройдут первый тест. Для первого аргумента первая функция (принимающая double) имеет худшую последовательность преобразования, чем вторая. Для второго аргумента вторая функция имеет более худшую последовательность преобразований, чем первая (опять же, int нужно продвигать до double в одном случае, но не в другом).

Следовательно, ни одна из функций не передает первое правило, а вызов неоднозначен.

Между второй парой функций каждый аргумент первой функции имеет как минимум хорошее преобразование в качестве аргумента сопоставления ко второй функции. Затем мы переходим ко второму правилу и находим, что есть хотя бы один аргумент (два, по сути), для которого первая функция имеет лучшее преобразование (идентичность вместо продвижения), чем вторая.

Следовательно, первая функция лучше сочетается и будет выбрана.

Ответ 3

Неоднозначность определяется ранжированием:

  • Точное совпадение: не требуется преобразование, преобразование lvalue-to-rvalue, преобразование квалификации, пользовательское преобразование типа класса в один класс
  • Продвижение: интегральное продвижение по службе, продвижение с плавающей запятой
  • Преобразование: интегральное преобразование, преобразование с плавающей точкой, преобразование с плавающим интегралом, преобразование указателя, преобразование указателя в элемент, логическое преобразование, преобразование определенного производного класса в его базу

Точное совпадение выигрывает против промоушена, который выигрывает против конверсии.

В примере:

void func(int, bool, float, int){cout << "int,bool,float,int" << endl;}
void func(int, bool, int, int){cout << "int,int,int,int" << endl;}

int main()
{
    func(1,1,3.4,4);
}

Аргумент 1 (1) является точным совпадением на обоих Аргумент 2 (1) является точным совпадением на обоих Аргумент 3 (3.4) можно преобразовать в float и int - Ambiguity. Ни то, ни другое. Аргумент 4 (4) является точным совпадением как на

Но если мы это сделали: func(1,1,3.4f,4);
(3.4f) теперь является точным совпадением!
void func(int, bool, float, int) затем выигрывает битва.