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

lar*_*moa 29 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;
}
Run Code Online (Sandbox Code Playgroud)

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

Sam*_*way 45

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

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

  • @GMan:返回该功能是标准所要求的(§25.1.1/ 2). (4认同)
  • FWIW符合c ++ 11标准的库将按照§25.2.4/ 3返回std :: move(f),因此可以保证能够从外部读取变异状态. (4认同)
  • 链接断开。 (3认同)

Cas*_*Cow 10

这可能是亚历克斯·斯捷潘诺夫有函数式编程范式,但你会发现,无论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; }   
};
Run Code Online (Sandbox Code Playgroud)

现在,如果你尝试:

MyFunctor f;
for_each( coll.begin(), coll.end(), f );
Y y = f.getValue();
Run Code Online (Sandbox Code Playgroud)

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

但是这种语言可以让你做到:

Y y = for_each( coll.begin(), coll.end(), MyFunctor() ).getValue();
Run Code Online (Sandbox Code Playgroud)

一切都很好,很方便.

要做同样的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() );
Run Code Online (Sandbox Code Playgroud)

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

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

Y * func( Y* py, X const & ); // function modifies *py in-place then returns py
Run Code Online (Sandbox Code Playgroud)

然后打电话:

Y y;
std::accumulate( coll.begin(), coll.end(), &y, func );
Run Code Online (Sandbox Code Playgroud)

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

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

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