Как определяется двусмысленность в алгоритме разрешения перегрузки?
Я пытаюсь понять метод разрешения перегрузки.
Почему это неоднозначно:
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)
затем выигрывает битва.