Ответ
Разрешение перегрузки (overload resolution) — это процесс выбора наиболее подходящей функции для данного вызова. Он происходит в несколько чётких этапов.
1. Поиск имён (Name Lookup): Компилятор находит все функции с указанным именем в текущей области видимости (с учётом пространств имён и ADL — Argument-Dependent Lookup).
2. Формирование набора кандидатов: Из найденных функций отбираются те, которые можно вызвать с данным числом аргументов (после учёта аргументов по умолчанию).
3. Ранжирование кандидатов: Для каждого кандидата определяется, насколько хорошо типы аргументов соответствуют типам параметров. Устанавливается «ранг» преобразования:
| Ранг преобразования | Пример (вызов f(arg)) |
Приоритет |
|---|---|---|
| Точное совпадение | Типы идентичны, включая const/volatile. f(int) ← f(42) |
Наивысший |
| Продвижение (Promotion) | Целочисленные: char→int, bool→int. f(int) ← f('a') |
Высокий |
| Стандартное преобразование | int→double, указатель→bool. f(double) ← f(42) |
Средний |
| Пользовательское преобразование | Через конструктор или оператор. f(MyClass) ← f(42) |
Низкий |
Многоточие (...) |
f(int, ...) ← f(42, 1.0) |
Наинизший |
4. Выбор наилучшей функции:
- Выбирается функция, для которой все преобразования аргументов имеют не хуже ранг, чем у любого другого кандидата.
- Если для какого-то кандидата есть хотя бы один аргумент с лучшим рангом преобразования, он побеждает.
- Если два кандидата одинаково хороши — возникает неоднозначность (ambiguity), и компилятор выдаёт ошибку.
Пример с ранжированием:
void process(int); // (1)
void process(double); // (2)
void process(long, int=0);// (3)
process(10); // Выбирает (1): точное совпадение (int ← int).
process(10.0f); // Выбирает (2): float → double (стандартное) лучше, чем float → int (стандартное) или float → long (стандартное). Ранги равны, но стандартные преобразования имеют разную «стоимость».
process(10L); // Ошибка неоднозначности: (2) требует long → double, (3) — точное совпадение по первому аргументу. Ни одна функция не лучше другой по всем аргументам.
Важные нюансы:
- Шаблонные функции участвуют в перегрузке. Нешаблонная функция обычно приоритетнее шаблонной при прочих равных.
- Учитываются ссылки, rvalue-ссылки и
const. - Преобразования, требующие
const_cast, не рассматриваются.
Ответ 18+ 🔞
А, ну ты глянь, какой интересный вопрос подкинули. Разрешение перегрузки, говоришь? Ну это, бля, целая операция, как в хорошем детективе — компилятор тут сыщик, который из кучи подозреваемых (функций) одного самого подходящего вычислить должен. Сейчас разжуём, как это всё внутри происходит, без соплей.
1. Поиск имён (Name Lookup): Всё начинается с того, что компилятор, этот хитрожопый ищейка, начинает шарить по всем закоулкам твоего кода. Ему надо найти всех, кто носит это имя — process, print, calculate, неважно. Смотрит в текущей области, лезет в неймспейсы, если они есть, и включает свой режим «ADL» — это когда он ещё и в пространствах имён, откуда пришли аргументы, ищет. Короче, собирает всех «в оцепление».
2. Формирование набора кандидатов: Дальше начинается отсев. Из всей этой толпы отбираются только те, кого вообще можно позвать с тем количеством аргументов, которое ты указал. Тут же учитываются аргументы по умолчанию — если функция говорит «я могу с двумя, но второй у меня по дефолту ноль», то её тоже в список кандидатов запишут. Остальные — свободны, нахуй.
3. Ранжирование кандидатов: Вот тут самое мясо. Каждого из оставшихся «подозреваемых» начинают проверять на совместимость с уликами (аргументами). Смотрят, насколько твои переданные типы похожи на то, что функция ждёт. И выставляют «ранг» — типа, насколько сильно пришлось извращаться, чтобы подогнать аргумент под параметр.
Смотри, вот тебе таблица, чтобы не охуеть от этого всего:
| Ранг преобразования | Пример (вызов f(arg)) |
Приоритет |
|---|---|---|
| Точное совпадение | Типы один в один, даже const/volatile на месте. f(int) ← f(42) |
Наивысший, идеал, мечта |
| Продвижение (Promotion) | Это как мелкие целочисленные типы (char, bool) превращаются в int. f(int) ← f('a') |
Высокий, почти почётно |
| Стандартное преобразование | Уже посерьёзнее: int в double, указатель в bool. f(double) ← f(42) |
Средний, бывает |
| Пользовательское преобразование | Вот тут уже через конструктор или оператор каст происходит. f(MyClass) ← f(42) |
Низкий, компилятор морщится, но терпит |
Многоточие (...) |
А это полный пиздец, «что угодно на вход». f(int, ...) ← f(42, 1.0) |
Наинизший, крайний случай, когда уже нихуя не подходит |
4. Выбор наилучшей функции: А теперь — момент истины, ёпта. Компилятор смотрит на ранги всех кандидатов.
- Победит та функция, у которой все преобразования для всех аргументов оказались не хуже, чем у любого другого конкурента.
- Если же нашёлся хоть один чувак, у которого для какого-то аргумента преобразование лучше (ранг выше), а для остальных не хуже — то он чемпион, остальные в утиль.
- А вот если вышла ничья — два кандидата одинаково хороши (или одинаково плохи) — компилятор просто разводит руками и орет: неоднозначность (ambiguity), ошибка, разбирайтесь сами, пидарасы.
Пример с ранжированием, чтобы вникнуть:
void process(int); // (1)
void process(double); // (2)
void process(long, int=0);// (3)
process(10); // Выберет (1): тут вообще сказка — точное совпадение (int ← int). Остальные даже рядом не стояли.
process(10.0f); // Выберет (2): тут float нужно к double привести (стандартное преобразование). Вариант (1) тоже требует float → int (стандартное), но преобразование к double считается «лучше» для float. Вариант (3) вообще мимо, ему long нужен.
process(10L); // А вот тут, ядрёна вошь, облом! (2) требует long → double (стандартное), а (3) — точное совпадение по первому аргументу (long ← long). Ни одна не лучше другой по всем параметрам — неоднозначность, пиздец.
Важные нюансы, без которых нихуя не понятно:
- Шаблонные функции тоже в этой движухе участвуют. Но если на арене есть и шаблонная, и обычная функция, и они одинаково подходят, то компилятор, как честный судья, обычно выбирает нешаблонную — она приоритетнее.
- Все эти ссылки (
&), rvalue-ссылки (&&) иconst— компилятор их видит и учитывает. Это тоже часть «точного совпадения». - А вот преобразования, которые требуют
const_cast— на них компилятор сразу забивает болт. Не, ну а чё, это уже твои проблемы, чувак.