在C++中利用Lambda函数提高代码机能
副标题#e#
使编译器以及操纵系统从正在建设的应用中榨取更高机能的要害在于提供富裕的有关代码意图的信息。在充实相识这个代码意图实现的成果等信息的环境下,就有大概将代码在编译时和运行时的并行吞吐量最大化,令开拓者可以将更多精神放在他们所存眷的贸易规模的问题,将重量级的多核多处理惩罚器的任务打算交托给编译器,运行时库以及操纵系统中的基本设施代码来处理惩罚。
轮回函数是很重要的一个环节,因为在所有可用的硬件资源中,被疏散的轮回中的各个部门在一般环境下可以或许提供更高的应用机能。思量这样一个小环境:迭代选定组合中的全部元素以求得总和。最简朴最直接的执行要领如下:
std::vector﹤int﹥ v;
v.push_back(1);
v.push_back(5);
int total = 0;
for (int ix = 0; ix ﹤ v.size(); ++ix){
total += v[ix];
}
以上的例子十分便于人工读写。对付熟悉C语言家属语法的开拓者而言,这个轮回的意图也十分容易领略。然而对付编译器以及运行时库的组合而言,要在多个线程之间打算好这个轮回,它还需要雷同于OpenMP编译指示一类的指示来汇报它那边有优化的空间:
std::vector﹤int﹥ v;
v.push_back(1);
v.push_back(5);
int total = 0;
#pragma omp for
for (int ix = 0; ix ﹤ v.size(); ++ix){
#pragma omp atomic
total += v[ix];
}
第一个OpenMP指示提出了多线程运行for轮回的要求,而第二个omp atomic指示则被用来防备多线程同时向总数变量上写入。对付OpenMP,在MSDN库的参考文档中有关于所有指示的具体先容。
假如利用了声明式轮回能力,那么将并行要领应用在矢量求和上则越发清洁简朴。STL for_each函数是一个抱负的替代品,以上的例子则被改写如下:
class Adder{
private:
int _total;
public:
Adder() : _total(0) {}
void operator ( ) ( int& i )
{
_total += i;
}
operator int ( )
{
return _total;
}
};
void VectorAdd()
{
std::vector﹤int﹥ v;
v.push_back(1);
v.push_back(5);
int total = std::for_each(v.begin(), v.end(), Adder());
}
这里,详细的for轮回被舍弃,求矢量和的代码变得清洁了一些;可是由于需要利用一系列运行符来界说一个类,这使得这个办理方案被大大的巨大化了。除非代码库中尚有大量雷同的求和声明,不然一个开拓者是不会仅仅为了STL for_each的那点长处而多耗费工夫去界说一个新类的。
仔细查抄这个Adder类,可以很明明的看出其大部门内容都仅仅是用来满意将实例用作函数工具的挪用条件的。这个类中独一起到计较浸染的仅仅是那一行_total += i.思量到这一点,C++ 0x提供了一个被大大简化了的、以lambda函数方法来实现的语法能力。Lambda函数移除了对这些摆架子代码的需求,并答允在别的的一个声明中界说一个谓词函数。由此,VectorAdd函数可以被改写如下:
std::vector﹤int﹥ v;
v.push_back(1);
v.push_back(5);
int total = 0;
std::for_each(v.begin(), v.end(),
[&total](int x) {total += x;}
);
#p#副标题#e#
Lambda函数的语法相当直截了当。方括号中的第一个lambda元素汇报编译器,当地变量total通过引用被捕获(这样的环境下最好用引用捕获,因为你需要矢量和的功效在for_each之后仍然有效),而lambda的第二部门则是参数列表。Lambda的最后一部门是函数的主体,这个例子中就是将参数x的值加到变量total中去。
假如在lambda函数中没有需要捕获的变量,可能只需要捕获变量的一个副本,那么函数开始的方括号可以留空:
std::for_each(v.begin(), v.end(), [](int x) {
std::cout ﹤﹤ x ﹤﹤ std::endl;
});
殽杂的捕获要领也可以利用:
int total = 0;
bool displayInput = true;
std::for_each(v.begin(), v.end(), [&total, displayInput](int x) {
total += x;
if (displayInput){
std::cout ﹤﹤ x ﹤﹤ std::endl;
}
});
这里,变量displayInput通过副本被捕获。Visual C++编译器在编译时会报错C3491:’displayInput’:一个在lambda函数内数值被改变的变量无法在一个非可变lambda中通过数值被捕获。
Lambda函数中尚有一个值得留意的处所,就是它的返回值范例。编译器一般会尽大概的(也是被要求的)揣度lambda表达式的返回值范例,不外对付巨大的多行表达式而言,有大概会需要确切的声明返回值范例。返回值范例声明通过在lambda函数参数和函数主体之间添加-﹥运行符以及需要被声明的返回值范例来实现:
std::for_each(v.begin(), v.end(),
[&](int x)-﹥void {total += x;});
}
#p#分页标题#e#
C++中有了lambda函数,这令声明式编程以及利用STL运算法例变得越发简捷。Lambda函数答允在函数主体内的可执行代码字行间举办界说。在为编译器提供强大的优化提示之外,Lambda函数所推崇的代码模式可以令人越发简朴的领略哪段代码是要实现奈何的成果。Visual C++ 2010将带来在并行处理惩罚上的显著成果晋升,而lambda函数将是详细实现这些晋升的重要手段之一。