将功能实现为函子的优缺点

Pros and Cons of implementing functionality as functor

本文关键字:优缺点 功能 实现      更新时间:2023-10-16

我正在和一位同事讨论一个只有一个公共方法的简单类的API。我最初选择了:

class CalculateSomething
{
public:
  void operator()(const SomeObject &obj) const;
private:
  // ...
}

但是,我的同事反对使用 operator((,为了清楚起见,希望简单地将该方法命名为"calculate"。虽然我觉得这个论点没有说服力,但它让我想到了利弊。

优点运算符((

  • 该类是精益的,并且有一个明确定义的目的。一旦实例化,它基本上充当一个自由函数。
  • 它是一个函子,可以很容易地这样使用(例如STL算法(。
  • 当它与范围算法一起使用时,可以直接传递对象,而不是通过函数指针传递,这使编译器能够内联代码。尽管不能保证,但通过函数指针传递它完全抑制了这种可能性。

缺点 运算符((

  • 如果不查看类名,则不太清楚该方法的作用。(我个人不同意,因为该类只有一个方法,因此从类名中可以清楚地看出其含义(
  • STL 中的大多数函子应该是无状态的。我认为这是阻碍我的主要原因......

我很惊讶地发现我对此的搜索并没有带来太多结果,因为我认为这是一个很常见的场景(一个类,一个责任(。因此,我真的很想听听其他人对此的看法。

如果 lambda 真的不是一种选择,您的选择应该取决于对象承担的工作范围......并导致您的编码约定或样式。你可以决定明确(参见Werolik的答案(,如果该方法相对陌生并且需要状态,这是一件好事,但是

让我们从标准库中获取简单的用例...

  • std::hash:这是一个函数对象,可以完成一项工作,并且应该做得很好,对此没有争议
  • 还有这么多...包括 std::less 及其分类

你看到的所有这些的共同点是它们是动词......在我看来,如果你的类和你发布的片段一模一样,CalculateSomething对我来说意味着一个动作,所以我总是可以把它实例化为CalculateSomething()(my_object...)

就像你引用的,它在使用 STL 算法本身和许多其他C++库时非常方便。如果你采用同事的方法,你可能不得不求助于使用std::binds和lambdas,因为你想"适应"一个接口。

例:

class CalculateSomething
{
    public:
         void operator()(const SomeObject &obj) const;
    private:
         // ...
}
class CalculateNothing
{
    public:
         void calculate(const SomeObject &obj) const;
    private:
         // ...
}

一个示例用法是:

std::for_each(container.begin(), container.end(), CalculateSomething());

std::for_each(container.begin(), container.end(), [ c = CalculateNothing()](auto x) { c.calculate(x); });

我想我更喜欢前者。

好吧,这里也是我的 5 美分。

首先,如果可能的话,我会简单地使用 lambda 或自由函数。您确定,您需要满足您需求的课程吗?

无论如何,假设该类是必需的。

  1. 我不太喜欢使用 operator((。也许还有其他评论。
  2. 可能最好将其重命名为" SomethingCalculator ' - 它不是一个对象吗?
  3. 然后你可以添加一个方法,比如'DoWork'、'Calculate'或其他什么。

这将使它更加明确。

上。以上所有内容仍然可以争论,但我真正相信的是,添加足够的代码内文档将产生真正的差异,与方法名称无关。