Представьте перегруженную функцию с параметрами long и double: почему вызов с аргументом типа int может быть неоднозначным, хотя оба преобразования допустимы?
Вызов неоднозначен, потому что преобразования из int в long и из int в double являются допустимыми стандартными преобразованиями одного общего ранга. Ни одна перегрузка не получает преимущества, поэтому компилятор не выбирает функцию произвольно и сообщает об ошибке неоднозначности.
Перегрузка функций появилась как способ связывать одну логическую операцию с несколькими типами данных, сохраняя единое имя интерфейса. Неявные преобразования дополняют этот механизм: вызывающему коду не всегда требуется вручную приводить аргумент к типу параметра.
Однако автоматический выбор допустим только тогда, когда у одной перегрузки есть объективное преимущество. Если такого преимущества нет, ошибка компиляции защищает от скрытого изменения смысла программы.
Для аргумента типа int обе функции подходят: значение можно преобразовать и в long, и в double. Но факт допустимости преобразования не означает, что компилятор обязан выбрать один из вариантов.
Если разработчик ошибочно ожидает выбор по размеру типа или по способности точно представить значение, программа не скомпилируется. Это особенно заметно после добавления новой перегрузки: ранее корректный вызов может стать неоднозначным.
При разрешении перегрузки компилятор сначала формирует набор подходящих функций, затем сравнивает последовательности неявных преобразований для каждой из них. Для int преобразование в long — стандартное целочисленное преобразование, а преобразование в double — стандартное преобразование целого числа в число с плавающей точкой.
Оба варианта имеют ранг conversion. Преобразование int в long не является целочисленным продвижением: продвижение применяется, например, при переходе от short или char к int, но не к произвольному более широкому целочисленному типу. Поэтому long не получает преимущество только из-за того, что он целочисленный.
Чтобы устранить неоднозначность, можно явно указать намерение приведением типа. Это локальное решение, но оно может скрыть ошибку или привести к потере точности при выборе double.
Другой вариант — добавить перегрузку для int, если это самостоятельный и семантически ожидаемый случай. Обычно это лучше для публичного интерфейса: выбор становится очевидным, а вызывающий код не зависит от ручных приведений.
Не следует рассчитывать на размер типов, диапазон значений или «естественность» преобразования для человека. При разрешении перегрузки важны правила ранжирования стандартных преобразований, а не субъективная близость типов.
В библиотеке обработки чисел уже существуют перегрузки для long и double. После добавления вызова с результатом функции, возвращающей int, сборка перестала проходить из-за неоднозначности.
Рассматривались три варианта:
long: быстро исправляет конкретный вызов, но решение нужно повторять в каждом месте и можно случайно выбрать неподходящую семантику;double: удобно для вычислений с дробной частью, но потенциально меняет точность и смысл операции;int: расширяет интерфейс, зато фиксирует ожидаемую семантику на уровне библиотеки.Если для целых значений должна выполняться отдельная целочисленная логика, выбирают третье решение. В результате вызовы с int становятся однозначными, а преобразования выполняются только там, где они действительно предусмотрены дизайном интерфейса.
Вопрос: Всегда ли преобразование из int в long имеет тот же ранг, что и преобразование из int в double?
Ответ: В рассматриваемой паре — да: оба являются стандартными преобразованиями ранга conversion. Размер long на конкретной платформе не превращает переход из int в long в promotion. Продвижение определяется специальными правилами для малых целочисленных типов, а не просто большей шириной целевого типа.
Вопрос: Что произойдет, если среди перегрузок появится функция с параметром int?
Ответ: Перегрузка с int будет выбрана для аргумента типа int, потому что ей не требуется преобразование: это точное соответствие. Точные соответствия имеют преимущество перед стандартными преобразованиями к long и double.
Вопрос: Может ли шаблонная перегрузка изменить результат выбора между long и double?
Ответ: Может, но только после учета правил сравнения невзаимозаменяемых кандидатов. Если шаблон выводит параметр ровно как int, он может получить точное соответствие и выиграть у нетemplate-перегрузок, которым нужны преобразования. Однако итог зависит от конкретных сигнатур и правил частичного упорядочивания шаблонов; наличие шаблона само по себе не делает его автоматически предпочтительным.