可能是 Alex Stepanov 具有函数式编程范式,但您会发现 std::accumulate 和 std::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 中的 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
{
// 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,而不是就地修改。顺便说一句,当就地修改确实更有效时,有一种解决方法,无需编写新算法(例如使用 += 或引用修改的累积 2),方法是使用以下函数签名:
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,您的仿函数最多将复制2份:一份作为函数的参数,一份作为返回值。我说“最多”是因为返回值优化可以减少这些副本中的第二个。使用accumulate,它会复制集合中的每个元素,即O(N),而不是固定时间。因此,如果副本稍微昂贵,则仿函数中的双重副本不会是在大型集合上迭代少量次的主要费用,而对于累积它会是(并且建议是指针破解)。