为什么要覆盖operator()?

Jef*_*ffV 52 c++ boost operator-overloading functor function-call-operator

Boost Signals库中,它们重载了()运算符.

这是C++中的约定吗?对于回调等?

我在同事的代码中看到了这一点(恰好是Boost的忠实粉丝).在那里的所有Boost善良中,这只会让我感到困惑.

有关这种超载原因的任何见解?

Luc*_*lle 135

重载operator()的主要目标之一是创建一个仿函数.仿函数就像一个函数,但它具有有状态的优点,这意味着它可以保持数据在调用之间反映其状态.

这是一个简单的仿函数示例:

struct Accumulator
{
    int counter = 0;
    int operator()(int i) { return counter += i; }
}
...
Accumulator acc;
cout << acc(10) << endl; //prints "10"
cout << acc(20) << endl; //prints "30"
Run Code Online (Sandbox Code Playgroud)

Functor大量使用泛型编程.许多STL算法都是以非常通用的方式编写的,因此您可以将自己的函数/仿函数插入到算法中.例如,算法std :: for_each允许您对范围的每个元素应用操作.它可以实现类似的东西:

template <typename InputIterator, typename Functor>
void for_each(InputIterator first, InputIterator last, Functor f)
{
    while (first != last) f(*first++);
}
Run Code Online (Sandbox Code Playgroud)

您会看到此算法非常通用,因为它是由函数参数化的.通过使用operator(),此函数允许您使用仿函数或函数指针.这是一个显示两种可能性的示例:

void print(int i) { std::cout << i << std::endl; }
...    
std::vector<int> vec;
// Fill vec

// Using a functor
Accumulator acc;
std::for_each(vec.begin(), vec.end(), acc);
// acc.counter contains the sum of all elements of the vector

// Using a function pointer
std::for_each(vec.begin(), vec.end(), print); // prints all elements
Run Code Online (Sandbox Code Playgroud)

关于你关于operator()重载的问题,是的,这是可能的.只要您遵守方法重载的基本规则(例如,不能仅在返回类型上进行重载),您就可以完美地编写具有多个括号运算符的仿函数.

  • 函数的另一个(通常是次要的)优点是函数可以简单地内联.没有涉及指针间接,只是在类上调用(非虚拟)成员函数,因此编译器可以确定调用哪个函数,并内联它. (3认同)
  • @MarkRansom但是,如果您改为传递一个仿函数,例如上面定义的`Accumulator`,那么`for_each`将被实例化为`Accumulator`,并且在其体内调用的函数是`Accumulator :: operator()(int )`.因此编译器知道将调用哪个函数,而不管在运行时传递的实际值.这使得它可以简单地内联呼叫 (3认同)

Lod*_*dle 25

它允许类充当函数.我在一个日志类中使用它,其中调用应该是一个函数,但我想要该类的额外好处.

所以这样的事情:

logger.log("Log this message");
Run Code Online (Sandbox Code Playgroud)

变成这样:

logger("Log this message");
Run Code Online (Sandbox Code Playgroud)


Jud*_*den 6

在 C++ 中使用 operator() 形成函子与函数式编程范例相关,函数式编程范例通常使用类似的概念:闭包


car*_*son 5

您还可以查看C++ 常见问题解答的矩阵示例。这样做有很好的用途,但这当然取决于您想要实现的目标。


Jor*_*ans 5

函子不是函数,因此不能重载它。
你的同事是正确的,尽管operator()的重载用于创建“函子”——可以像函数一样调用的对象。与期望“类似函数”参数的模板相结合,这可能非常强大,因为对象和函数之间的区别变得模糊。

正如其他发帖者所说:函子比普通函数有一个优势,因为它们可以拥有状态。此状态可用于单次迭代(例如计算容器中所有元素的总和)或多次迭代(例如查找多个容器中满足特定条件的所有元素)。


Mar*_*som 5

许多人已经回答它是一个仿函数,没有说明为什么仿函数比普通的旧函数更好的一个重要原因.

答案是仿函数可以有状态.考虑求和函数 - 它需要保持运行总计.

class Sum
{
public:
    Sum() : m_total(0)
    {
    }
    void operator()(int value)
    {
        m_total += value;
    }
    int m_total;
};
Run Code Online (Sandbox Code Playgroud)

  • 杰夫五:方便.这意味着可以使用相同的语法来进行调用,无论我们是调用函子还是函数指针.例如,如果您查看std :: for_each,它可以使用仿函数或函数指针,因为在这两种情况下,调用的语法都是相同的. (5认同)
  • 这并不能解释为什么需要隐藏它是一个对象并伪装成一个函数的事实. (3认同)