Перегрузка функций (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 неоднозначен.