25

我最近发现,在 C++ 中,您可以重载“函数调用”运算符,以一种奇怪的方式,您必须编写两对括号才能这样做:

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

然后以这种方式使用它:

A a;
a();

这什么时候有用?

4

7 回答 7

34

这可用于创建“函子”,即像函数一样工作的对象:

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。上面链接的维基百科文章提供了更多实质性示例。

于 2010-02-28T02:59:02.760 回答
21

在您开始使用模板之前,使用 operator() 只会获得语法上的好处。但是在使用模板时,您可以以相同的方式对待真正的函数和仿函数(充当函数的类)。

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) );
于 2010-02-28T03:09:32.537 回答
6

使用模板实现的算法并不关心被调用的是函数还是仿函数,它关心的是语法。标准的(例如 for_each())或您自己的。函子可以有状态,在被调用时可以做各种事情。函数只能具有带有静态局部变量或全局变量的状态。

于 2010-02-28T03:06:07.893 回答
1

如果您正在创建一个封装函数指针的类,这可能会使用法更加明显。

于 2010-02-28T02:56:52.833 回答
1

编译器还可以内联仿函数和函数调用。但是,它不能内联函数指针。这样,在与标准 C++ 库算法一起使用时,使用函数调用运算符可以显着提高性能。

于 2017-03-27T10:32:00.723 回答
1

例如实现生成器:

// 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;
}
于 2018-04-05T23:26:29.510 回答
0

我看到了另一种异国用途的潜力:

假设您有未知类型的对象,并且必须声明另一个相同类型的变量,如下所示:

 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() 重定向到自己类型的构造函数。

于 2018-09-18T09:16:49.797 回答