我尝试在代码中转换一些循环以使用STL的for_each功能。目前,我在同一组数据上计算和累加两个单独的值,要求我循环数据两次。为了速度,我想循环一次并积累两个值。建议使用for_each,因为它显然可以很容易地用于多线程或多处理器实现(我还没有学会如何做到这一点。)
创建一个只循环数据并计算两个值的函数很容易,但我需要返回两者。要与for_each一起使用,我需要在每次迭代时返回两个计算值,以便STL可以对它们求和。根据我的理解,这是不可能的,因为for_each期望返回一个值。
除了更清晰的代码(可以说是?)之外,使用for_each的目标是最终转移到多线程或多处理器实现,以便可以并行完成数据循环,从而使事情运行得更快。
有人建议我使用仿函数而不是函数。但是,这引发了两个问题。
谢谢!
答案 0 :(得分:6)
以下是使用仿函数并行执行两次累加的示例。
struct MyFunctor
{
// Initialise accumulators to zero
MyFunctor() : acc_A(0), acc_B(0) {}
// for_each calls operator() for each container element
void operator() (const T &x)
{
acc_A += x.foo();
acc_B += x.bar();
}
int acc_A;
int acc_B;
};
// Invoke for_each, and capture the result
MyFunctor func = std::for_each(container.begin(), container.end(), MyFunctor());
[请注意,您还可以考虑使用std::accumulate()
,并为operator+
添加适当的重载。]
对于虚拟仿函数,你不能直接这样做,因为STL函数按值而不是通过引用来获取仿函数(因此你会遇到切片问题)。你需要实现一种“代理”仿函数,而这种仿函数又包含对你的虚拟函子的引用。 * 类似于:
struct AbstractFunctor
{
virtual void operator() (const T &x) = 0;
};
struct MyFunctor : AbstractFunctor
{
virtual void operator() (const T &x) { ... }
};
struct Proxy
{
Proxy(AbstractFunctor &f) : f(f) {}
void operator() (const T &x) { f(x); }
AbstractFunctor &f;
};
MyFunctor func;
std::for_each(container.begin(), container.end(), Proxy(func));
* Scott Meyers在他出色的Effective STL的第38项中给出了一个很好的例子。
答案 1 :(得分:4)
好的,我最终做了三个(主要)实现(有很小的变化)。我做了一个简单的基准测试,看看是否有任何效率差异。查看底部的基准测试部分
std::for_each
采用一些c ++ 0x快捷方式:请参阅http://ideone.com/TvJZd
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
std::vector<int> a = { 1,2,3,4,5,6,7 };
int sum=0, product=1;
std::for_each(a.begin(), a.end(), [&] (int i) { sum+=i; product*=i; });
std::cout << "sum: " << sum << ", product: " << product << std::endl;
return 0;
}
打印
sum: 28, product: 5040
正如其他人所说,你通常更喜欢正常的循环:
for (int i: a)
{ sum+=i; product*=i; }
这两者都是
另外,在非c ++ 11 / 0x中非常接近:
for (std::vector<int>::const_iterator it=a.begin(); it!=a.end(); ++it)
{ sum+=*it; product*=*it; }
std::accumulate
根据std::accumulate
添加了一个:请参阅http://ideone.com/gfi2C
struct accu_t
{
int sum, product;
static accu_t& handle(accu_t& a, int i)
{
a.sum+=i;
a.product*=i;
return a;
}
} accum = { 0, 1 };
accum = std::accumulate(a.begin(), a.end(), accum, &accu_t::handle);
std::accumulate
std::tuple
好的我无法抗拒。这是一个accumulate
,但在std::tuple
上运行(不需要仿函数类型):请参阅http://ideone.com/zHbUh
template <typename Tuple, typename T>
Tuple handle(Tuple t, T v)
{
std::get<0>(t) += v;
std::get<1>(t) *= v;
return t;
}
int main()
{
std::vector<int> a = { 1,2,3,4,5,6,7 };
for (auto i=1ul << 31; i;)
{
auto accum = std::make_tuple(0,1);
accum = std::accumulate(a.begin(), a.end(), accum, handle<decltype(accum), int>);
if (!--i)
std::cout << "sum: " << std::get<0>(accum) << ", product: " << std::get<1>(accum) << std::endl;
}
return 0;
}
通过累积2 <&lt;&lt; 31次测量(参见基于std :: tuple的变体的片段)。仅使用-O2和-O3进行测试:
所示的任何方法(0.760s)之间没有可测量的差异:
for (int i:a)
accu_t
结构(0.760s)std::tuple
所有变体都表现出从-O2到-O3(13.8s到0.760s)的加速度超过18x ,无论选择的实施方式如何
tuple
/ accumulate
表现与Tuple& handle(Tuple& t, T v)
(通过引用)完全相同。