Functor与std :: bind

时间:2013-07-23 12:18:36

标签: c++ c++11 boost-bind

有时我倾向于编写仿函数,不是为了在函数调用之间维护状态,而是因为我想捕获函数调用之间共享的一些参数。举个例子:

class SuperComplexAlgorithm
{
    public:
        SuperComplexAlgorithm( unsigned int x, unsigned int y, unsigned int z )
            : x_( x ), y_( y ), z_( z )
        {}

        unsigned int operator()( unsigned int arg ) const /* yes, const! */
        {
            return x_ * arg * arg + y_ * arg + z_;
        }

    private:
        // Lots of parameters are stored as member variables.
        unsigned int x_, y_, z_;
};

// At the call site:
SuperComplexAlgorithm a( 3, 4, 5 );
for( unsigned int i = 0; i < 100; ++i )
    do_stuff_with( a ); // or whatever

相比
unsigned int superComplexAlgorithm( unsigned int x, unsigned int y,
                                    unsigned int z, unsigned int arg )
{
    return x * arg * arg + y * arg + z;
}

// At the call site:
auto a = std::bind( superComplexAlgorithm, 3, 4, 5, std::placeholders::_1 );
for( unsigned int i = 0; i < 100; ++i )
    do_stuff_with( a );

第一种解决方案在我看来有许多缺点:

  • xyz所做的文档在不同的地方分开(构造函数,类定义,operator())。
  • 很多锅炉代码。
  • 不太灵活。如果我想捕获不同的参数子集怎么办?

是的,我刚刚意识到boost::bindstd::bind有多么有用。现在在我开始在我的很多代码中使用它之前的问题:是否有任何情况我应该考虑在普通函数中使用手写的无状态函子来绑定参数?

2 个答案:

答案 0 :(得分:8)

lambda解决方案将是自动的C ++ 11方式:

auto a = [&]( int x ){ return superComplexAlgorithm( 3, 4, 5, x ); };
for( unsigned int i = 0; i < 100; ++i )
  do_stuff_with( a );

使用手写的仿函数的好处是你可以:

  1. 将参数移动到捕获中,或者转换它们(你可以在C ++ 1y中使用lambda,但还没有 - 使用bind也可以这样做)
  2. 它具有非匿名类型(因此您可以在不使用auto的情况下讨论该类型) - bind也是如此,但它也有非匿名你可以在没有decltype整个表达式的情况下获得有意义的类型! C ++ 1y再次使bind / lambda。
  3. 更好
  4. 您可以完美转发剩余的参数(您可以在C ++ 1y中使用lambda执行此操作,bind执行此操作)
  5. 你可以搞乱捕获数据的存储方式(在指针中?智能指针?),而不会弄乱你创建实例的代码。
  6. 你也可以使用bind和lambdas无法做的手写编写器来做一些非常强大的事情,比如将一组多个函数的重载包装到一个对象中(可能有也可能不共享一个名字)但是这种角落的情况不会经常出现。

答案 1 :(得分:4)

  

是否有任何情况我应该考虑在普通函数中使用手写无状态函子来绑定参数?

来自评论:

  

我不想在调用站点定义superComplexAlgorithm,因为它非常复杂,在很多地方使用,需要测试和文档等。

如果superComplexAlgorithm的实现需要一堆代码,并且最终将它拆分为带有大量参数的不同函数,那么最好使用一个提供跨共享状态的类。内部实施功能。除了那个角落的情况,std::bind的行为将等同于你在问题中提供的仿函数。

有些说明,因为您提到boost::bindstd::bind作为替代方案。您可能想测试您的实现的功能。例如,使用boost 1.35(古代),bind操作将为每个参数创建4个副本,VS2010将在std::bind中创建7个副本,尽管gcc 4.7中的std::bind仅执行1个。参数很小,不会产生很高的成本,但如果你正在进行操作,或者你的对象复制成本很高......只需要测量。

关于lambda,如果性能是一个问题,也要衡量它的行为方式。它应该制作一份副本。如果性能不是问题,那么lambda在捕获的内容上就不那么明确(用户需要读取lambda的实现来解决这个问题),甚至查看代码也可能不那么明显。 / p>