【问题标题】:Should I use functions or stateless functors?我应该使用函数还是无状态函子?
【发布时间】:2011-04-02 07:41:31
【问题描述】:

这两段代码做同样的事情。如您所见,它将用于排序功能。 哪个更好?我通常写后一个。但是我看到一些程序员像以前那样做。

struct val_lessthan : binary_function<pair<string,int>, pair<string, int>, bool>
{
    bool operator() (const pair<string,int>& x, const pair<string,int>& y) const
    {
        return x.second < y.second;
    }
} val_lt;

bool val_lt(const pair<string,int>& x, const pair<string,int>& y) 
{
    return x.second < y.second;
}

会像这样使用它:

std::sort(wordvector.begin(), wordvector.end(), val_lt);

【问题讨论】:

  • 最简单的工作就是最好的。
  • 您没有提到应根据哪些标准来判断备选方案。有几种情况,其中任何一种都比另一种“更好”……但总的来说,是的,选择更简单的一种。如果考虑任一替代方案,请不要忘记 c++1x lambdas。

标签: c++ function stl functor


【解决方案1】:

您看到有些人更喜欢第一个版本的原因是仿函数可以简单地内联。

当您将函子传递给std::sort 时,函子类型对于函数是已知的,因此在编译时也知道要调用的确切函数,并且可以简单地内联。

对于一个普通函数,std::sort 真正看到的只是一个函数指针,并且在编译时它没有说明它指向的 哪个 函数。因此,除非编译器执行一些相当广泛的流分析以查看指针来自 在此特定调用中 的位置,否则不能内联。它肯定会在像你这样的小例子中进行优化,但是如果函子/函数指针是从其他地方作为函数参数传入的,或者它是在传递给@987654323之前从中间数据结构中读取的@,那么,编译器可能无法内联函数指针版本,因此最终会变慢。

【讨论】:

    【解决方案2】:

    第一个称为函数对象,如果您需要将任何上下文信息传递给比较函数,它会很有用。独立函数只能获取xy,没有机会携带任何上下文。

    在上面的具体实例中,比较函数的两种写法大致等价。

    【讨论】:

      【解决方案3】:

      作为一项规则,我可能更喜欢第一个,但通常更喜欢使用模板:

      template <class T>
      struct val_lessthan : binary_function<pair<pair<T, T>, bool> {
          bool operator()(T const &x, T const &y) const { 
             return x.second < y.second;
          }
      };
      

      .second 的使用限制了通用性的程度,但您仍然会得到一点点(例如,如果没有记错,boost::tuple 为两个元素的元组提供了 .first.second。通常,模板可以更好地确保编译器能够内联生成代码,因此如果您关心效率,它可能会有所帮助(或者可能不会,但不太可能造成任何伤害)。

      【讨论】:

        【解决方案4】:

        如果您希望能够在代码的其他部分调用该函数,而不是作为函子传递,请首选函数形式。例如,您更喜欢:

        if (val_lt(a,b))
        {
        //...
        }
        

        if(val_lessthan()(a,b))
        {
        // ...
        }
        

        否则在选择函子形式时,最好使用未命名的函子对象进行调用。那就是:

        std::sort(wordvector.begin(), wordvector.end(), val_lesstthan());
        

        代替:

        val_lesstthan named;
        std::sort(wordvector.begin(), wordvector.end(), named);
        

        取消命名参数和返回值很容易使编译器执行优化。它指的是一个称为 RVO(返回值优化)的全局概念。在这种情况下,它可能会将您的代码从一个副本结构中解放出来。

        【讨论】:

        • Baumes:实际上,鉴于对象是无状态的,编译器可能会完全忽略构造本身。
        • @Matthieu M. 谢谢,完全没有考虑到这一点。来自索菲亚?我在那里学习.. :-)
        【解决方案5】:

        我会说,选择最适合您的特定情况的最简单的。在这种情况下,请选择第二个而不是第一个。

        【讨论】:

          【解决方案6】:

          两者的速度都一样快。几乎可以忽略不计的差异。

          当你使用functor时,意味着函数operator()在编译器生成的代码中有三个参数,第一个参数是指向val_lt对象本身的指针,第二个和第三个参数是你设置的参数'在签名中提到。像这样的:

          //the possible code generated by the compiler!
          bool operator() (val_lessthan *_this, const pair<string,int>& x, const pair<string,int>& y) const
                        //^^^^^^^^^^^^^^^^^^^ note this!
          {
              return x.second < y.second;
          }
          

          【讨论】:

          • 除了仿函数中的 operator() 是内联的
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-01-12
          • 2016-02-20
          • 1970-01-01
          • 1970-01-01
          • 2015-03-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多