December 4, 2020

Перегрузка оператора ()

Перегрузка функций обеспечивает механизм создания и выполнения вызовов функций с одним и тем же именем, но с разными параметрами. Это позволяет одной функции работать с несколькими разными типами данных (без необходимости придумывать уникальные имена для каждой из функций).

int subtract(int a, int b){
    return a - b;
}
 
double subtract(double a, double b){
    return a - b;
}

В языке C++ операторы реализованы в виде функций. Используя перегрузку функции оператора, вы можете определить свои собственные версии операторов, которые будут работать с разными типами данных (включая пользовательские типы). Это называется перегрузкой оператора.

Перегрузка операторов позволяет нам определить самим тип параметров, при этом количество аргументов должно быть фиксировано. Например, оператор == всегда принимает два параметра (бинарный оператор), тогда как оператор ! всегда принимает один параметр (унарный оператор). Оператор () является особенно интересным, поскольку позволяет изменять как тип параметров, так и их количество.

Но следует помнить о двух вещах:

  • Перегрузка круглых скобок должна осуществляться через метод класса.
  • Оператор () является оператором вызова функции. В случае с классами перегрузка круглых скобок выполняется в методе operator()(){} (в объявлении функции перегрузки находятся две пары круглых скобок).

Перегрузка оператора () используется в реализации функторов (или «функциональных объектов») — классов, которые работают как функции. Преимущество функтора над обычной функцией заключается в том, что функторы могут хранить данные в переменных-членах (поскольку они сами являются классами). Вот пример использования простого функтора:

class Accumulator{
private:
    int mcounter = 0;
public:
    Accumulator() { }
    int operator() (int i) {
        return (mcounter += i);
    }
};
 
int main(){
    Accumulator accum;
    cout << accum(30) << endl; // выведется 30
    cout << accum(40) << endl; // выведется 70
}

Использование класса Accumulator выглядит так же, как и вызов обычной функции, но наш объект класса Accumulator может хранить значение, которое увеличивается.

Конечно, можно было просто создать функцию и использовать в ней статическую локальную переменную. Однако, поскольку функции представлены только одним глобальным экземпляром (т.е. нельзя создать несколько объектов функции), использовать эту функцию мы можем только для выполнения чего-то одного за раз. С помощью функторов мы можем создать любое количество отдельных функциональных объектов, которые нам нужны, и использовать их одновременно.