可能Alex Stepanov有函数式编程的范式,但您会发现std::accumulate
和std::for_each
都是通过值传递而不是引用传递它们的操作数(函数和累加值)。
class MyFunctor
{
Y val;
public:
MyFunctor() : val() {}
void operator()( X const& 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中的val设置为引用,将其创建在循环外并将其传递给MyFunctor。
但是,该语言允许您直接执行以下操作:
Y y = for_each( coll.begin(), coll.end(), MyFunctor() ).getValue();
方便易用,一行搞定。
使用std::accumulate
实现相同功能的代码如下:
class MyFunctor2
{
public:
Y operator()( Y y, X const& x ) const
{
...
}
};
Y y = std::accumulate( coll.begin(), coll.end(), Y(), MyFunctor2() );
你可以使用一个函数(或者在C++11中使用lambda表达式)代替functor。请注意,这里的functor没有状态,你将初始化的对象作为参数传递进去,它可以是一个临时变量。
现在我们知道Y是可复制的。std::accumulate对Y使用按值传递,而不是就地修改。顺便说一句,当就地修改确实更有效率时,有一种解决方法可以避免编写新算法(例如使用+=或引用修改的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 );
我们“知道”返回值将是 &y。如果我们想在一个地方访问 Y 的成员,我们可以利用这一点,例如。
Y y;
Z z = std::accumulate( coll.begin(), coll.end(), &y, func )->getZ();
顺便提一下,在for_each
和accumulate
的复制中,一个关键区别是它们所要进行的复制的复杂度或数量。在for_each
中,最多会有两个副本被创建:一个作为函数参数,另一个作为返回值。我说“最多”的原因是,返回值优化可能会减少这两个副本中的第二个。而在accumulate
中,它会将每个元素都进行复制,即O(N)
而不是常数时间。因此,如果复制相对昂贵,那么在大型集合上迭代少量次数时,functor中的双重复制不会成为主要开销,而对于累加器(accumulate)而言,则会导致较大的开销(建议使用指针技巧)。
<numeric>
中使用std::accumulate
,它允许你在这种情况下在函数对象外维护状态。 - Potatoswatter