为什么std :: for_each(from,to,function)返回函数?

时间:2010-01-12 12:48:47

标签: c++ templates stl

我刚刚阅读了std::for_each的代码:

template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f)
{
  for ( ; first!=last; ++first ) f(*first);
  return f;
}

并且无法看到此模板函数返回输入函数的任何充分理由。有没有人有这方面有用的例子?

6 个答案:

答案 0 :(得分:45)

允许您在函数中累积状态,然后将其返回到您的调用代码。例如,你的函数(作为一个函子类)可以有一个成员int来计算它被调用的次数。

以下是一个包​​含一些示例的页面:http://xenon.arcticus.com/c-morsels-std-for-each-functors-member-variables

答案 1 :(得分:10)

Alex Stepanov可能有函数式编程范例,但你会发现std::accumulatestd::for_each都按值传递它们的操作数(函数和累加值),而不是参考。因此:

class MyFunctor
{
   Y val;
   public:
     MyFunctor() : val() {}

     void operator()( X const& x )
     {
        // do something to modify val based on x
     }

     Y getValue() const { return val; }   
};

现在,如果你尝试:

MyFunctor f;
for_each( coll.begin(), coll.end(), f );
Y y = f.getValue();

它不起作用,因为for_each一直在处理f的副本。当然,你可以在内部有一个shared_ptr<Y>的实例,因此它指向同一个实例。您也可以在MyFunctor中创建一个引用,在循环外创建它并将其传递给MyFunctor。

然而,该语言可以让您:

Y y = for_each( coll.begin(), coll.end(), MyFunctor() ).getValue();

美观,方便,一条线。

std::accumulate做同样的事情会这样做:

class MyFunctor2
{
public:
      Y operator()( Y y, X const& x ) const
      {
         //    create a new Y based on the old one and x
        ...
      }
};

Y y = std::accumulate( coll.begin(), coll.end(), Y(), MyFunctor2() );

您可以使用函数(或在C ++ 11中使用lambda)而不是函子。请注意,这里的仿函数没有状态,并且将初始化对象作为参数传递,这可能是暂时的。

现在我们知道Y是可复制的。 std::accumulate在Y上使用by value,而不是就地修改。顺便提一下,当就地修改确实更有效时,有一种解决方法,无需编写新的算法(例如使用+ =或引用修改的accumulate2),使用以下函数签名:

Y * func( Y* py, X const & ); // function modifies *py in-place then returns py

然后打电话:

Y y;
std::accumulate( coll.begin(), coll.end(), &y, func );

我们“知道”返回值将是&amp; y。如果我们想在一个地方访问Y的成员,我们就可以利用这个,例如。

Y y;
Z z = std::accumulate( coll.begin(), coll.end(), &y, func )->getZ();

顺便提一下,for_each中的副本和accumulate中的副本的主要区别在于它将复制/复制的数量。使用for_each,最多可以有2个由仿函数组成的副本:一个作为函数的参数,一个作为返回函数。我说“最多”,因为返回值优化可以减少这些副本中的第二个。对于accumulate,它会与集合中的每个元素一起复制,即O(N)而不是恒定时间。因此,如果副本的价格相当昂贵,那么仿函数中的双重副本将不会成为大型集合中重复次数的主要费用,而对于累积它将是(并且建议将是指针黑客)。

答案 2 :(得分:3)

返回该函数基本上使std::for_each成为std::accumulate的平庸模仿。它允许您在函数/仿函数中累积某些内容,然后在完成后检索该累积值。几乎在您认为这可能是有用的事情时,您应该考虑使用std::accumulate代替。

答案 3 :(得分:2)

如果你想在调用之间保存(以及稍后使用)仿函数状态,它很有用,例如,计算集合中元素的数量,或者通过设置一些内部变量来指示处理元素的失败。

答案 4 :(得分:2)

如果传入一个函数对象,也就是仿函数,并且它具有状态,则返回函数对象允许您在迭代序列后访问它的状态。假设你有一个函数对象,它从序列中计算出三个不同的变量,并将它们保存在成员变量中。每次调用仿函数时,都会更新计数器。如果for_each没有返回对象,你会如何得到结果?

注意......这就是为什么你必须始终实现复制构造,并为具有状态的函数对象赋值。

答案 5 :(得分:-4)

我猜没有特别的原因。你可以做的是在另一个foreach调用中使用返回的函数,从而避免写两次函数名,并可能在那里犯错。