Что такое перегрузка функций и операторов в C++?

«Что такое перегрузка функций и операторов в C++?» — вопрос из категории C++ Core, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Перегрузка (Overloading) в C++ — это возможность определять несколько функций или операторов с одним и тем же именем, но с разными списками параметров (по типам, количеству или порядку). Компилятор выбирает подходящую версию на основе контекста вызова (аргументов).

1. Перегрузка функций

#include <iostream>
#include <string>

// Разное количество параметров
void print(int value) {
    std::cout << "Integer: " << value << std::endl;
}
void print(double value) { // Разный тип параметра
    std::cout << "Double: " << value << std::endl;
}
void print(const std::string& label, int value) { // Разный порядок/набор
    std::cout << label << ": " << value << std::endl;
}

int main() {
    print(42);          // Вызовет print(int)
    print(3.14);        // Вызовет print(double)
    print("Count", 42); // Вызовет print(const std::string&, int)
}

2. Перегрузка операторов

Позволяет определять поведение операторов (+, -, <<, [], (), etc.) для пользовательских типов.

class Vector2D {
public:
    double x, y;
    Vector2D(double x, double y) : x(x), y(y) {}

    // Перегрузка оператора сложения как член класса
    Vector2D operator+(const Vector2D& other) const {
        return Vector2D(x + other.x, y + other.y);
    }

    // Перегрузка оператора вывода в поток как дружественная функция
    friend std::ostream& operator<<(std::ostream& os, const Vector2D& v) {
        os << "(" << v.x << ", " << v.y << ")";
        return os;
    }

    // Перегрузка оператора вызова функции (functor)
    double operator()() const {
        return std::sqrt(x*x + y*y); // Возвращает длину вектора
    }
};

int main() {
    Vector2D v1(1.0, 2.0), v2(3.0, 4.0);
    Vector2D v3 = v1 + v2; // Используется operator+
    std::cout << "v3 = " << v3 << std::endl; // Используется operator<<
    std::cout << "Length of v1: " << v1() << std::endl; // Используется operator()
}

Ключевые правила и ограничения:

  • Перегружать можно только существующие операторы, нельзя создавать новые (например, **).
  • Приоритет и ассоциативность оператора остаются неизменными.
  • Нельзя перегружать по возвращаемому типу. Выбор перегрузки зависит исключительно от типов и количества аргументов.
  • Некоторые операторы можно перегружать только как функции-члены класса (например, operator=), другие — как члены или свободные функции (например, operator+).
  • Перегрузка должна быть интуитивно понятной. Например, operator+ не должен вычитать элементы.