繁体   English   中英

Functor与std :: bind

[英]Functors vs. std::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 do的文档在不同的地方被分开(构造函数,类定义, operator() )。
  • 大量的锅炉板代码。
  • 不太灵活。 如果我想捕获不同的参数子集怎么办?

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

lambda解决方案将是idomatic 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. 你可以完美地转发其余的参数(你可以在C ++ 1y中使用lambda来完成这个,并且bind做到这一点)
  4. 你可以搞乱捕获数据的存储方式(在指针中?一个智能指针?),而不会弄乱你创建实例的代码。

你也可以用一些无法用bind和lambdas做的手写编写器做一些非常强大的东西,比如将一组多个函数的重载包装到一个对象中(可能有也可能没有共享一个名字),但是那种角落情况不会经常出现。

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

来自评论:

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

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

一些注释,因为你提到boost::bindstd::bind作为替代品。 您可能想测试您的实现的功能。 例如,使用boost 1.35(古代) bind操作将为每个参数创建4个副本,VS2010将在std::bind 7个副本,尽管gcc std::bind中的std::bind仅执行1.如果参数小则赢得'这需要很高的成本,但是如果你正在进行操作,或者你的物品复制成本很高......只需要测量。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM