对容器中所有元素的成员函数结果求和的最佳方法是啥?

Posted

技术标签:

【中文标题】对容器中所有元素的成员函数结果求和的最佳方法是啥?【英文标题】:What's the best way to sum the result of a member function for all elements in a container?对容器中所有元素的成员函数结果求和的最佳方法是什么? 【发布时间】:2011-03-13 09:40:50 【问题描述】:

假设我有以下对象:

struct Foo

    int size()  return 2; 
;

获得vector<Foo> 中所有对象的总size 的最佳方法是什么(最可维护、可读等)?我会发布我的解决方案,但我对更好的想法感兴趣。

更新:

到目前为止,我们有:

std::accumulate 和仿函数 std::accumulate 和 lambda 表达式 普通的 for 循环

还有其他可行的解决方案吗?你能用boost::bindstd::bind1st/2nd 做一些可维护的东西吗?

【问题讨论】:

std::vector<Foo> vec; vec.size() * 2,因为我们知道Foo::size 总是返回 2。:) 【参考方案1】:

使用 C++11(及更高版本)基于范围的 for 循环

std::vector<Foo> vFoo;
// populate vFoo with some values...
int totalSize = 0;
for (const auto& element: vFoo) 
    totalSize += element.size();

【讨论】:

喜欢这个解决方案。我发现 std::accumulate 需要额外的脑力:需要知道容器的类型(Foo)。千万不要搞砸了初始值的类型。打字时间更长,因此可以阅读。需要在 lambda 的参数上使用 cbegin/cend() 和 const 以确保 const 性。【参考方案2】:

除了你自己的建议,如果你的编译器支持 C++0x lambda 表达式,你可以使用这个更短的版本:

std::vector<Foo> vf;

// do something to populate vf


int totalSize = std::accumulate(vf.begin(),
                                vf.end(),
                                0, 
                                [](int sum, const Foo& elem) return sum + elem.size(););

【讨论】:

错字:lambda 正文末尾缺少分号(我无法自己编辑)。【参考方案3】:

我发现 Boost 迭代器很优雅,尽管它们可能有点冗长(基于范围的算法会更好)。在这种情况下transform iterators 可以完成这项工作:

#include <boost/iterator/transform_iterator.hpp>
//...

int totalSize = std::accumulate(
    boost::make_transform_iterator(vf.begin(), std::mem_fn(&Foo::size)),
    boost::make_transform_iterator(vf.end(), std::mem_fn(&Foo::size)),0);

编辑:将“boost::bind(&amp;Foo::size,_1)”替换为“std::mem_fn(&amp;Foo::size)

编辑:我刚刚发现 Boost.Range 库已经更新以引入范围算法!这是同一解决方案的新版本:

#include <boost/range/distance.hpp> // numeric.hpp needs it (a bug?)
#include <boost/range/numeric.hpp> // accumulate
#include <boost/range/adaptor/transformed.hpp> // transformed
//...
int totalSize = boost::accumulate(
    vf | boost::adaptors::transformed(std::mem_fn(Foo::size)), 0);

注意:性能大致相同(请参阅我的评论):在内部,transformed 使用 transorm_iterator

【讨论】:

我对这个解决方案和直接解决方案进行了时间比较,不幸的是这个解决方案比较慢(我发现一个介于 2 和 5 之间的因素)。但是,这可能不是问题。 我认为这是最好的答案。问题是要累积 what,这是由自定义迭代器解决的,而不是 如何累积,这是通过使用仿函数来解决的。默认的累积行为(加号)你想要的。考虑将此问题扩展到内积:转换后的迭代器是可重用的,而仿函数则不是。每个算法都需要一个新的函子,只需根据成员 size() 重新定义默认行为。【参考方案4】:

这里是实际的解决方案:

typedef std::vector<Foo> FooVector;
FooVector vf;
int totalSize = 0;
for (FooVector::const_iterator it = vf.begin(); it != vf.end(); ++it) 
  totalSize += it->size();

【讨论】:

【参考方案5】:

使用std::accumulate 和仿函数。

#include <functional>
#include <numeric>

struct SumSizes : public std::binary_function<int, Foo, int>

    int operator()(int total, const Foo& elem) const
    
        return total + elem.size();
    
;

std::vector<Foo> vf;

// do something to populate vf

int totalSize = std::accumulate(vf.begin(),
                                vf.end(),
                                0, 
                                SumSizes());

【讨论】:

当然,您的解决方案是最惯用的解决方案,但在这种简单的情况下,愚蠢的迭代器循环可能更容易。 +1 这可以通过模板化SumSizes 来改进通用性,因为所有标准容器都有一个size() 成员函数。 @Jon,我想你可能误解了这个问题。关键不是要获取容器的大小,而是要对所有元素的成员函数的结果求和。也许size 对这样的功能来说是个糟糕的名字。 不,我理解这个问题,只是觉得我会提出一个奇怪的观点,因为您的示例恰好使用了标识符size()。如果设为通用,SumSizes 将汇总容器容器(或序列,例如std::string)的每个元素的各个大小。顺便。 :P

以上是关于对容器中所有元素的成员函数结果求和的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在我的网站上使用的所有纸张元素中全局更改墨水颜色的最佳方法是啥?

使用子查询保存查询结果的最佳方法是啥?

从 ActionScript 数组中删除所有元素的最佳方法?

数组求和

条目五《尽量使用区间成员函数代替它们的单元素兄弟》

如何对数组列表的元素求和?