类的operator()或将函数绑定为functor?

3

有两种方法可以创建一个保持状态的函数(functor):

  1. 绑定一个函数并定义一个状态:bind(f, _1, state)

    例如,有以下代码:

    double g(double x, double state) {
      return x+state;
    }
    function f = bind(g,_1,state);
    
  2. 使用 () 运算符和一个类:

    请参考以下代码:

struct f { 
  double state; 
  f(double state_):state(state_) {} 
  double operator()(double x) {return x+state;}
};

我发现bind方法写起来更快,但我想知道是否存在一些隐藏的问题,因为在大多数文献中,我看到将函数对象作为类的()运算符。

你的 std::bind(f, _1, state) 中的 f 是什么? - kennytm
std::bind 的作用基本上就像在您的第二个案例中创建一个对象。因此实际上并没有太大的区别。第二种情况在书籍等方面更为常见,因为 std::bind 和 C++11 仍然是相当新的。 - Some programmer dude
1
由于标记为C+11,还有另一种方法,使用带有捕获的lambda函数。 - Casper Beyer
3个回答

3
第三种方式是使用lambda表达式:
auto f = [state]( double x ) { return x * state; };

这是一个不错的快速解决方案,但我不知道如何在不同的函数中使用它。似乎不够模块化 - 必须在函数定义中定义状态。在另外两个方法中,您可以在初始化时定义状态,并将函数定义在单独的模块中。 - kirill_igum

1
我认为bind是受到了函数式语言的启发(就像头文件的名称一样)。我认为它相当等效,因为它是一个模板函数,但可能优化了内置调用......
这是我第一次看到这个函数,所以我需要查看汇编代码以查看差异,然后我会重新发布 ;)
尽管如此,它不允许您在函数对象中拥有其他方法,因此operator()对于许多用途仍然是必需的。 [编辑] 好吧,我看到汇编:与“传统方式”相比,绑定添加了很多代码。因此,我建议您使用结构体的方式来使用(即只是一个函数对象)。此外,这种代码更易理解。如果您利用参数替换,则绑定是很好的,但是对于简单的使用,它就像激光炮一样切割奶酪:P [/编辑]

如果我使用 -O2 优化,我会得到相同的速度。你是在有或没有优化的情况下查看汇编代码?感谢您的关注。 - kirill_igum

0

看起来使用struct是更快的方法:

11:01:56 ~/try 
> g++ -std=c++11 main.cpp ; ./a.out
in 2265 ms, functor as a struct result = 1.5708e+16
in 31855 ms, functor through bind result = 1.5708e+16
11:02:33 ~/try 
> clang++ -std=c++11 main.cpp ; ./a.out
in 3484 ms, functor as a struct result = 1.5708e+16
in 21081 ms, functor through bind result = 1.5708e+16

这段代码:

#include <iostream>
#include <functional>
#include <chrono>

using namespace std;
using namespace std::placeholders;
using namespace std::chrono;

struct fs {
  double s;
  fs(double state) : s(state) {}
  double operator()(double x) {
    return x*s;
  }
};

double fb(double x, double state) {
  return x*state;
}

int main(int argc, char const *argv[]) {
  double state=3.1415926;

  const auto stp1 = system_clock::now();
  fs fstruct(state);
  double sresult;
  for(double x=0.0; x< 1.0e8; ++x) {
    sresult += fstruct(x);
  }
  const auto stp2 = high_resolution_clock::now();
  const auto sd = duration_cast<milliseconds>(stp2 - stp1);  
  cout << "in " << sd.count() << " ms, "; 
  cout << "functor as a struct result = " << sresult << endl;

  const auto btp1 = system_clock::now();
  auto fbind = bind(fb, _1, state);
  double bresult;
  for(double x=0.0; x< 1.0e8; ++x) {
    bresult += fbind(x);
  }
  const auto btp2 = high_resolution_clock::now();
  const auto bd = duration_cast<milliseconds>(btp2 - btp1);  
  cout << "in " << bd.count() << " ms, "; 
  cout << "functor through bind result = " << bresult << endl;

  return 0;
}

更新(1)

一个函数也可以是一个函数对象:

struct fbs {
  double operator()(double x, double state) const {
    return x*state;
  }
};

在 main.cpp 中:

  const auto bstp1 = system_clock::now();
  auto fbindstruct = bind(fbs(), _1, state);
  double bsresult;
  for(double x=0.0; x< 1.0e8; ++x) {
    bsresult += fbindstruct(x);
  }
  const auto bstp2 = high_resolution_clock::now();
  const auto bsd = duration_cast<milliseconds>(bstp2 - bstp1);  
  cout << "in " << bsd.count() << " ms, "; 
  cout << "functor through bind-struct result = " << bsresult << endl;

速度没有改变:

> g++ -std=c++11 main.cpp ; ./a.out
hi
in 2178 ms, functor as a struct result = 1.5708e+16
in 31972 ms, functor through bind result = 1.5708e+16
in 32083 ms, functor through bind-struct result = 1.5708e+16
12:15:27 ~/try 
> clang++ -std=c++11 main.cpp ; ./a.out
hi
in 3758 ms, functor as a struct result = 1.5708e+16
in 23503 ms, functor through bind result = 1.5708e+16
in 23508 ms, functor through bind-struct result = 1.5708e+16

更新(2)

添加优化后,时间相似:

> g++ -std=c++11 -O2 main.cpp ; ./a.out
hi
in 536 ms, functor as a struct result = 1.5708e+16
in 510 ms, functor through bind result = 1.5708e+16
in 472 ms, functor through bind-struct result = 1.5708e+16
12:31:33 ~/try 
> clang++ -std=c++11 -O2 main.cpp ; ./a.out
hi
in 388 ms, functor as a struct result = 1.5708e+16
in 419 ms, functor through bind result = 1.5708e+16
in 456 ms, functor through bind-struct result = 3.14159e+16

GCC 4.8.1和Clang 3.3

注意 Clang 3.3在“bind-struct”情况下会给出错误的结果

更新(3)

有没有基于宏的适配器可以从类中制作一个函数对象?中进行了更多性能测试。


你的基准测试结果并不是很有意义,因为有很多情况:bind可以与可变数量的参数一起使用,要全面检查,需要根据参数数量进行检查。 但这还不是全部,有些用法并不是函数。 请参见http://www.cplusplus.com/reference/functional/bind/。 - hl037_
这只是一个起始想法。从答案中,我会使用bind,因为编写原型更快,然后如果需要进一步优化,我会测量其他选项。 - kirill_igum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接