【问题标题】:The benefit of function objects?函数对象的好处?
【发布时间】:2013-04-28 12:00:24
【问题描述】:

我知道 STL 中使用的函数对象只是简单的对象,我们可以像函数一样操作它。那我可以说函数和函数对象的工作是一样的。如果是真的,那为什么我们应该使用函数对象而不是函数呢?

【问题讨论】:

    标签: c++ function object stl


    【解决方案1】:

    主要的好处是对函数对象(函子)的调用通常是可内联的,而对函数指针的调用通常不是(主要示例是将 C 的 qsort 与 C++ 的 std::sort 进行比较)。对于非平凡的对象/比较器,C++ 应该会扼杀 C 的排序性能。

    还有其他好处,例如,您可以将状态绑定或存储在函子中,而原始函数则无法做到这一点。

    编辑 抱歉没有直接参考,但 Scott Meyers 声称在某些情况下有 670% 的改进: Performance of qsort vs std::sort?

    编辑 2 带有性能说明的段落是这样的:

    函数指针参数禁止内联的事实解释了一个 长期 C 程序员经常难以相信的观察结果: 当涉及到 C++ 的排序时,几乎总是让 C 的 qsort 感到尴尬 速度。当然,C++ 有函数和类模板来实例化和 调用看起来很有趣的 operator() 函数,而 C 使一个简单的 函数调用,但所有 C++“开销”都被吸收了 汇编。在运行时,sort 对其比较进行内联调用 函数(假设比较函数已被声明为内联 并且它的主体在编译期间可用)而 qsort 调用它的 通过指针进行比较函数。最终结果是那种 跑得更快。在我对一百万个双精度向量的测试中,它跑起来了 快 670%,但不要相信我的话,你自己试试吧。它的 在比较函数对象和实际函数时易于验证 作为算法参数,有一个抽象的好处。

    -Scott Meyers "Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library" - Item 46
    

    【讨论】:

    • @PhilipDahnen:我认为这主要是理论上的,函数指针在编译时你不知道它指向哪里,因此除了微不足道的情况外不能内联。函子具有嵌入在类型中的函数,因此在所有情况下都可以简单地内联。
    • 正如@PhilipDahnen 所说,从假设的角度来看,这主要是理论上的。在实践中,您应该查看为您的案例生成的程序集,以确保。如果您在优化的构建中查看生成的程序集,它应该很明显。注意:这可能只有在所讨论的仿函数的实现是可内联的时才会产生影响。
    • 有些情况下编译器不会内联函数:例如调试构建。或者,由于大小、复杂性等原因,某些编译器不会内联某些函数,甚至标记为内联。inline 不是命令,它是对编译器/链接器的提示。
    【解决方案2】:

    函数对象相对于函数的好处是它可以保持状态(来自wikipedia):

    #include <iostream>
    #include <iterator>
    #include <algorithm>
    
    class CountFrom {
    private:
      int &count;
    public:
      CountFrom(int &n) : count(n) {}
      int operator()() { return count++; }
    };
    
    int main() {
      int state(10);
      std::generate_n(std::ostream_iterator<int>(std::cout, "\n"), 11, CountFrom(state));
      return 0;
    }
    

    常规函数不能像函数对象那样保持状态。如果我没记错的话,那是没有 lambda 和闭包的方法(在 C++11 wikipedia section 之前)...

    【讨论】:

    • 是的,我明白第一句话。但接下来我看不懂。你能给我解释一下吗?谢谢。
    • Lambda 是内联声明的无名函数。 C++11 添加了 lambda,其中一个特点是它可以“捕获”变量并在函数中使用它们。维基百科有一篇关于closures的文章
    【解决方案3】:

    我认为函子最好的一点是它们可以在内部存储信息。在没有std::bind 的那些日子里,人们必须编写大量一元比较函数,以便将其传递给某些例程,例如remove_if

    【讨论】:

      【解决方案4】:

      http://cs.stmarys.ca/~porter/csc/ref/stl/function_objects.html

      STL 使用函数对象(函子)作为排序/搜索容器的回调。 函子是模板,因此更容易实现为类。试着用函数指针说greater&lt;T&gt;...考虑到 STL 中的容器也是模板。

      【讨论】:

      • @MooingDuck:是的,你的意思!然而我说的是指针类型,这也没什么大不了的,我明白了......
      • 在我的示例中greater&lt;int&gt; 的类型是bool (*)(const int&amp;, const int&amp;),但是是的,除此之外你的答案很好。
      • @MooingDuck:是否可以在全局范围内声明模板函数指针?喜欢错误的template&lt;typename T&gt; typedef bool (*cmp)(const T&amp;, const T&amp;);
      • 模板'template cmp'不是一个函数,所以你不能有一个指向它的指针。但是像 'greater' 这样的实例化(不是正确的词,忘记了正确的词)是一个函数,因此可以有一个指针。在 C++11 中,你可以使用 'using' 给模板起别名。
      猜你喜欢
      • 1970-01-01
      • 2015-02-21
      • 1970-01-01
      • 2012-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多