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


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

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

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 преобразования.

Так почему же один неоднозначен, а другой нет? В чем тут логика?

3 61

3 ответа:

правила разрешения перегрузки определяют только частичный порядок на множестве всех совпадений-если перегрузка F1 не лучший матч, чем F2, это не означает, что F2 Это лучший матч, чем F1. Точный частичный порядок можно рассматривать как сравнение двух точек в k размеры, где число аргументов k. Давайте определим этот частичный порядок по точкам в k-Дим пространства - (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.)

формулировка из стандарта (§[over.спичка.best] / 1) это:

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

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

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

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

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

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

  1. точное соответствие: не требуется преобразование, преобразование lvalue в rvalue, преобразование квалификации, пользовательское преобразование типа класса в тот же класс
  2. промотирование: объединенное промотирование, промотирование с плавающей запятой
  3. преобразование: интегральное преобразование, преобразование с плавающей запятой, преобразование с плавающей запятой, преобразование указателя, преобразование указателя в элемент, булево преобразование, определяемое пользователем преобразование производного класса в его базу

точное совпадение выигрывает против продвижения, которое выигрывает против преобразования.

пример:

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);
}