Перегрузка оператора вызова функции?
Недавно вычитал про перегрузку оператора вызова функции operator(). И мне кажется это абсолютно бесполезно.
Не могли вы пожалуйста подсказать для чего это нужно делать и в каких случаях?
Ответы (3 шт):
На пример, когда делают двумерные массивы склеенные в одномерный, для функторов вот статья на хабре и для другого рада облегчения жизни.
operator() - это соглашение для удобства использования шаблонного кода. Некий шаблонный алгоритм для своей кастомизации принимает что-то, что может быть вызвано как функция, т.е. через запись с круглыми скобками (). Кол-во параметров, передаваемых внутри этих круглых скобок определяется задачей. Например, для алгоритма поиска std::find_if в качестве предиката можно передать обычную функцию, функтор (объект, в классе которого реализован как раз operator(), или лямбду:
#include <functional>
#include <vector>
#include <algorithm>
bool f(int) { return true; }
struct F {
bool operator()(int) { return true; }
};
auto l = [](int) { return true; };
int main() {
std::vector<int> v;
std::find_if(v.begin(), v.end(), f); // обычная функция
std::find_if(v.begin(), v.end(), F()); // функтор
std::find_if(v.begin(), v.end(), l); // лямбда
}
При этом шаблонный код (алгоритма find_if) один и тот же для всех типов, которые можно использовать через нотацию вызова функции - круглые скобки. Т.е. как раз то, что предоставляет operator().
В стандартной библиотеке довольно много классов, где реализована перегрузка operator(). Например:
- разные распределения случайных величин:
bernoulli_distribution,uniform_real_distribution...; - различные унарные и бинарные (арифметические/логические/битовые) операции:
std::logical_not,std::bit_and,std::less,std::minus...; - обёртка над ссылкой
std::reference_wrapper; - конечно же, обёртка над функциями
std::function
и т.п.
operator() довольно полезный. Если мы хотим представить объект класса как функцию, нам нужно перегрузить оператор круглые скобки. Это называеться функтор(функциональный объект).
"А для чего?" - спросите вы. Это облегчает жизнь: вместо logger.log("Log this message");, оптимальней будет logger("Log this message");. Вместо divider.divide(T, T) - просто divider(T, T);. Также удобно с двумерными массивами - 2x2_array(0, 4) = 62;.
A std::function? Это продвинутый ООП-объект-указатель на функцию. И когда этому объекту мы присвоили какую-то функцию, как вызывать эту функцию через std::function? С помощию перегруженого оператора ()!
Это понятие широко используеться в STL. Одним из популярнейших прменений функторов является передача их в качестве аргументов алгоритмам. Тем самым можно регулировать поведение.