Перегрузка функций (function overloading) — несколько функций с одним именем и разными списками параметров. При вызове компилятор сам выбирает подходящую версию по аргументам.
Зачем нужно
Swap для int и Swap для float делают одно и то же. Без перегрузки
пришлось бы придумывать имена вроде SwapInt и SwapFloat. С перегрузкой имя
одно, а нужная версия выбирается по типам аргументов.
Как работает
Перегружать можно по количеству аргументов и по типам аргументов:
void Swap(int& lhs, int& rhs);
void Swap(float& lhs, float& rhs);
void Swap(float& x); // другое количество
void PrintValue(double x);
void PrintValue(int x);
void PrintValue(long x);
void PrintValue(long x, long y);Различать функции можно только тем, что входит в сигнатуру. Возвращаемый тип
не входит: int F(int) и float F(int) — конфликт, а не перегрузка.
Выбор перегрузки
Из комментария в коде лекции: выбирается наиболее подходящая по параметрам
версия, требующая наименьшего числа преобразований (conversion). Расширение
(promotion) — не преобразование. float → double — расширение;
float → long double и double → long double — преобразования.
float y = 0;
int x = 0;
PrintValue(y); // double: float → double — расширение
PrintValue(x); // int: точное совпадение
PrintValue(y, y); // (long, long): единственная с двумя параметрамиБез версии с double вызов PrintValue(y) стал бы неоднозначным:
float → int и float → long — одинаково «плохие» преобразования.
Ошибка в коде лекции:
int → longКомментарий в
main.cppназывает расширением иint → long. Это преобразование: целочисленное расширение — толькоbool,char,short→int, см. Арифметические преобразования типов. Проверка на GCC 16.2: приP(long)иP(double)вызов сintнеоднозначен. Подробнее — L05 - Функции и ссылки.
Не из лекции
Ранги по каждому аргументу: точное совпадение > расширение > преобразование. Побеждает версия, которая по каждому аргументу не хуже остальных и хотя бы по одному лучше. Если такой нет — ошибка
call of overloaded ... is ambiguous. Проверено на GCC 16.2:shortиcharпри перегрузкахP(long)/P(double)тоже неоднозначны, потому что их расширение ведёт вint, аintздесь нет.
Константность параметра
При выборе перегрузки константность самого параметра игнорируется.
int* const — константность на параметре, игнорируется. const int* —
константность объекта, на который указываем, — нет.
void H(int x);
void H(const int x); // та же функция: redefinition, если обе с телом
void G(int* p);
void G(const int* p); // другая функция: настоящая перегрузкаШаблоны и обычные функции
Из шаблона компилятор порождает новые перегрузки. Если обычная функция и шаблон подходят одинаково хорошо, выигрывает обычная функция.
Тонкости
- Аргументы по умолчанию не создают перегрузок:
int Min(int x = 0, int y = 1)— одна функцияMin(int, int). Совместить её с отдельнойMin(int)— верный способ получить неоднозначный вызовMin(5).
Не из лекции
Пункт про аргументы по умолчанию — вывод из правил, а не утверждение лекции.
Частые ошибки
- Перегружать по возвращаемому типу.
- Ждать, что
int«расширится» доlongлучше, чем преобразуется вdouble. - Писать перегрузки
f(int)иf(const int)как разные функции. - Оставить набор перегрузок, в котором вызов с
floatнеоднозначен.