7

我最近对仿函数感到兴奋,并一直在使用它们。然后出现了我需要我的仿函数执行两个不同的操作的情况,我考虑向我的仿函数添加另一种方法(而不是重载 () 运算符)。这是否是不好的做法我不确定(也许你可以告诉我),但这让我开始思考为什么我首先使用函子而不仅仅是对象。所以我的问题是:

重载 () 运算符有什么特别之处,还是只是在语法上比使用普通命名方法更具吸引力?

更新:

首先,我知道为什么函子可能比其他问题中解释的函数指针更可取。我想知道为什么它们比具有命名方法的对象更可取。

其次,作为一个例子,当我想使用我的函子的另一个可能命名的方法时:基本上我有两个函数,一个计算称为图形分区的模块化的东西 -compute_modularity()另一个计算一些变化后的模块化增益的分区compute_modularity_gain()。我想我可以将这些函数作为同一个函子的一部分传递给优化算法,并将增益作为命名函数。我不只是将两个函子传递给算法的原因是我想强制它compute_modularity_gain()仅与另一个函子结合使用compute_modularity(),而不是另一个函子,例如compute_stability()(只能与compute_stability_gain(). 换句话说,增益函数必须与其兄弟函数紧密耦合。如果有另一种方法可以强制执行此约束,请告诉我。

4

3 回答 3

7

重载的原因operator()是让仿函数具有与函数指针相同的调用语义——事实上,如果你愿意,你可以使用函数指针。

重载而不是使用函数有几个原因operator()——但最重要的一个是编译器在使用函数指针时很少会优化间接函数调用,但他们几乎总是会优化operator()调用——这是为什么std::sort通常节拍std::qsort

这有很多复杂的原因,但真正归结为大多数(不是?)编译器实现了删除函数指针调用的可能优化,这在现代硬件上是昂贵的。

然后情况出现了,我需要我的仿函数来执行两个不同的操作

那么它就不再是函子了。要么传递两个函子来做你想做的事,要么定义一个模板方法类。(您也可以使用 mixins 在 C++ 中实现模板方法模式,而无需运行时开销——但 Wikipedia 文章并未涵盖这一点)(另请注意:与 C++ 模板不同,但如果您使用 C++ 模板,则可以使用AOP 路由)

于 2011-04-05T01:26:57.913 回答
1

函子背后的基本意图是将知道如何执行某种工作的代码与知道何时需要完成该工作的代码分离(经典示例是将函子与 UI 按钮相关联)。

仿函数模型的一个小好处是普通的旧函数指针已经是仿函数。包装它们不需要额外的工作。我认为这是一个小好处,因为 a) 函数指针的效率略低于直接调用函数,并且 b) 我发现我几乎总是需要将某种形式的状态绑定到我要包装的任何东西上,即使它只是this成员函数的指针。

一元接口的主要优点是它作为函子的生产者和消费者的通用语言。比如说,你可以定义函子,让所有的函子都有一个invoke()成员函数,但是其他一些人会决定标准化 . do(),而另一些人可能会选择call(). 所有这些解决方案都涉及更多的打字。

此外,从不严格要求单个“函子”上的多个成员函数。如果某些代码需要调用多个不同的操作,您可以简单地传递多个函子。这提供了很好的灵活性,因为这些操作可能是耦合的,也可能是完全不相关的。

一个解耦的例子是一个需要相等比较器和哈希函数的哈希表。在这种情况下,这两个函数可能是不相关的:包装类的operator==()相等性和包装一个自由函数来计算散列。

一个耦合的例子是一个 UI 组件,它发出几个不同的事件。单个类可能会响应所有事件,或者不同的类可能会响应不同的事件组。函子使选择任一模型变得容易,而需要一个定义所有组件事件回调的“接口”则更加尴尬。如果单个对象想要以不同的方式处理来自两个组件的事件,Functors 也会变得更容易,因为您可以为每个组件提供一组不同的 functor 包装的成员函数。

doX()最后,将现有功能包装在仿函数中是很好理解的,并且受到诸如 boost.bind 之类的库的广泛支持,同时创建实现和doY()不实现的丢弃类。此外,新标准添加了 lambda,这极大地简化了仿函数的创建。

于 2011-04-05T01:18:16.760 回答
0

函子的唯一特别之处是它们可以像函数一样使用。然而,仿函数也可能通过其构造函数注入信息。

您可能还想研究 std::function (或 boost::function 如果您的编译器尚不支持它)可用于调整具有匹配调用签名的任何类型的对象。

std::bind 或 boost::bind 允许您将具体参数与函数的参数相关联,这与通过仿函数的构造函数传递它们的效果相同。您甚至可以使用 bind 来提供指向成员函数的 this 指针,这样它们就可以像普通函子一样被调用,而无需显式指定对象。

于 2011-04-05T00:47:36.763 回答