Чем может быть полезно перегрузить оператор «вызова функции»? - PullRequest
24 голосов
/ 28 февраля 2010

Я недавно обнаружил, что в C ++ вы можете перегрузить оператор «вызова функции», странным образом, для этого вам нужно написать две пары скобок:

class A { 
  int n;
public: 
  void operator ()() const; 
};

А затем используйте это так:

A a;
a();

Когда это полезно?

Ответы [ 7 ]

27 голосов
/ 28 февраля 2010

Это можно использовать для создания "функторов" , объектов, которые действуют как функции:

class Multiplier {
public:
    Multiplier(int m): multiplier(m) {}
    int operator()(int x) { return multiplier * x; }
private:
    int multiplier;
};

Multiplier m(5);
cout << m(4) << endl;

Вышеуказанные отпечатки 20. В статье Википедии, приведенной выше, приведены более содержательные примеры.

17 голосов
/ 28 февраля 2010

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

class scaled_sine
{
    explicit scaled_sine( float _m ) : m(_m) {}
    float operator()(float x) const { return sin(m*x); }
    float m;
};

template<typename T>
float evaluate_at( float x, const T& fn )
{
   return fn(x);
}

evaluate_at( 1.0, cos );
evaluate_at( 1.0, scaled_sine(3.0) );
5 голосов
/ 28 февраля 2010

Алгоритм, реализованный с использованием шаблона, не заботится о том, является ли вызываемая вещь функцией или функтором, он заботится о синтаксисе. Либо стандартные (например, for_each ()), либо ваши собственные. И функторы могут иметь состояние и делать разные вещи, когда их вызывают. Функции могут иметь состояние только со статической локальной переменной или глобальными переменными.

1 голос
/ 27 марта 2017

Компилятор также может встроить функтор и вызов функции. Однако он не может встроить указатель на функцию. Таким образом, использование оператора вызова функции может значительно улучшить производительность, когда он используется, например, со стандартными алгоритмами C ++ libary.

1 голос
/ 28 февраля 2010

Если вы создаете класс, инкапсулирующий указатель на функцию, это может сделать использование более очевидным.

0 голосов
/ 18 сентября 2018

Я вижу потенциал для еще одного экзотического использования:

Предположим, у вас есть объект неизвестного типа, и вам нужно объявить другую переменную того же типа, например:

 auto c=decltype(a*b)(123);

Когда такой шаблон широко используется, decltype становится очень раздражающим. Этот случай может возникнуть при использовании некоторой системы интеллектуального типа, которая автоматически изобретает тип результата функций и операторов на основе типов аргументов.

Теперь, если каждая специализация каждого типа этого типа системы оснащена магическое определение operator() как это:

template<????> class Num<???>{
    //specific implementation here
    constexpr auto operator()(auto...p){return Num(p...);}
}

decltype() больше не нужно, вы можете написать просто:

auto c=(a*b)(123);

Потому что operator () объекта перенаправляет на конструктор своего собственного типа.

0 голосов
/ 06 апреля 2018

Например, для реализации генераторов:

// generator
struct Generator {
    int c = 0;

    virtual int operator()() {
        return c++;
    }
};

int sum(int n) {
    Generator g;

    int res = 0;
    for( int i = 0; i < n; i++ ) {
        res += g();
    }

    return res;
}
...