Как определяется неоднозначность в алгоритме разрешения перегрузки?
Я пытаюсь понять метод разрешения перегрузки.
почему это неоднозначная:
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 ответа:
правила разрешения перегрузки определяют только частичный порядок на множестве всех совпадений-если перегрузка
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в одном случае, но не в других).поэтому ни одна из функций не передает первое правило, и вызов неоднозначен.
между второй парой функций каждый аргумент первой функции имеет по крайней мере такое же хорошее преобразование, как и соответствующий аргумент второй функции. Затем мы переходим ко второму правилу и обнаруживаем, что существует по крайней мере один аргумент (два, по сути), для которого первая функция имеет лучшее преобразование (идентичность вместо продвижения), чем второй.
поэтому первая функция является лучшим соответствием и будет выбрана.
неоднозначность определяется ранжированием:
- точное соответствие: не требуется преобразование, преобразование lvalue в 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); }