当前位置:首页 > 芯闻号 > 充电吧
[导读]以前,在lambda表达式没有进入标准的时候,对容器的遍历等涉及到使用函数指针的情况,一般人会懒得使用std::for_each,或std::transform,也许只是一个短短的几句话,却要单独写个

以前,在lambda表达式没有进入标准的时候,对容器的遍历等涉及到使用函数指针的情况,一般人会懒得使用std::for_each,或std::transform,也许只是一个短短的几句话,却要单独写个函数,或函数对象,写的代码反而不如自己用for循环来的快。
但是,C++11引入了lambda表达式后,一切都变的简单了!

1.lambda表达式 lambda表达式是一个匿名函数,用它可以非常方便的表示一个函数对象,先简单说一下lambda表达式,下面这张图表示了C++11中lambda表达式的写法


Lambda表达式的引入标志,在‘[]’里面可以填入‘=’或‘&’表示该lambda表达式“捕获”(lambda表达式在一定的scope可以访问的数据)的数据时以什么方式捕获的,‘&’表示一引用的方式;‘=’表明以值传递的方式捕获,除非专门指出。

Lambda表达式的参数列表

Mutable 标识(可以没有)

异常标识(可以没有)

返回值,如果没有,可以不写

“函数”体,也就是lambda表达式需要进行的实际操作

下面看看几个lambda表达式的例子

void print(int a){……}
上面函数lambda表达式为:

[](int a){……}
上面这个是无返回值的例子,下面这个是有返回值的例子
int add(int a,int b){……}
上面函数lambda表达式为:
[](int a,int b)->int{……}
当需要引入其他变量的时候,如有个类的成员变量需要引用或者函数局部变量这种情况下可以显示声明需要引入的变量
如成员变量:
double m_result;
函数:

void foo(int a,double b)
{
    ……
    use(m_result);
}
其lambda表达式可以表示为
[m_result](int a,int b){……use(m_result);}
如果lambda表达式需要修改m_result的值,可以以引用方式传递进去
[&m_result](int a,int b){……m_result = 12.1;……}
如果要传入的参数很多,或者干脆在这个作用域里的所有参数都想用到,可以直接在中括号里使用”=“或”&“ 如下例子,g_bb为一个全局变量,fun3的lambda表达式把所有内容以引用方式传入:
double g_bb = 11.2;
void foo1()
{
    auto f_add = [&](int a,int b)->int{return a+b;};
    std::cout<double{return aa+3;};//此时aa不能进行赋值操作如:aa=7;
    std::cout<double{aa = 7.0;return aa+3;};//此时aa以引用方式传入,可以进行赋值操作如:aa=7,同时修改aa的值;
    std::cout<double{aa = 8.0;g_bb = 15;return aa+3;};//此时aa可以进行赋值操作如:aa=7;,其他在作用域范围的变量都可以以引用方式调用
    std::cout<
输出:

3
8  aa:5
10  aa:7
11  aa:8 g_bb:15

一般也是以这种方式来写[&],简单明了。 lambda表达式先说到这后面在讲std::for_each和std::transform时会有更多例子。 2.std::foreach std::foreach是很经典的算法,但是由于需要用到函数对象,有时候还不如直接for循环方便(暂且不讨论for循环的新表达式写法,目前没多少个编译器支持),例如我有个vector,我要打印出来看看里面有什么内容,经常下意识的就直接成:
std ::vector v ;
v . push_back ( 3);
v . push_back ( 1. 666);
v . push_back ( 4. 5);
v . push_back ( 6. 7);
for( std ::vector ::iterator i =v . begin (); i !=v . end (); ++i )
{
    std ::cout <<*i <<",";
}

std :: vector < double >:: iterator是多么的碍眼的。 还好,C++11把auto升了级,上面那个代码会变成
for( autoi =v . begin (); i !=v . end (); ++i )

用std::foreach(),上面这个就变成

std ::for_each ( v . begin (), v . end (),[ &]( double d ){ std ::cout <

于是,以后凡是要遍历容器,且代码不太长,都可以使用std::foreach加lambda表达式方便实现
为了方便下面的演示,编写一个打印容器内容的函数printElement
template
void printElement(Container& v)
{
    std::cout<<"(";
    for(auto i = v.begin();i != v.end();++i)
    {
        std::cout<<*i;
        if(i != v.end() - 1)
            std::cout<<",";
    }
    std::cout<<")";
    std::cout<

可以输出序列的内容,如vector内容为(1,2,3,4,5),输出:(1,2,3,4,5) 3.std::transform
当涉及到两个或三个容器的操作,就需要使用std::transform操作 transform有两个重载版本
template 
  OutputIterator transform (InputIterator first1, InputIterator last1,
                            OutputIterator result, UnaryOperation op);

原理如下图所示:



template ;
OutputIterator transform (InputIterator1 first1, InputIterator1 last1,
                            InputIterator2 first2, OutputIterator result,
                            BinaryOperation binary_op);
原理如下图所示:



如需要求序列a的log,结果存入c

//求a的log存入c
    a.clear();b.clear();c.clear();
    for(auto i(1);i<10;++i){
        a.push_back(i);
    }

    std::transform(a.begin(),a.end(),std::back_inserter(c),[](double d)->double{return log(d);});
    std::cout<<"a:";
    printElement(a);//
    std::cout<<"c:";
    printElement(c);//(0,0.693147,1.09861,1.38629,1.60944,1.79176,1.94591,2.07944,2.19722)


如果对a序列求log并直接把结果存入a中的话有两种方法 方法1:使用(transform)
 std::cout<<"a=log(a):"<double{return log(d);});
    std::cout<<"after a:";
    printElement(a);//(0,0.693147,1.09861,1.38629,1.60944,1.79176,1.94591,2.07944,2.19722)
输出:

a=log(a):
a:(1,2,3,4,5,6,7,8,9)
after a:(0,0.693147,1.09861,1.38629,1.60944,1.79176,1.94591,2.07944,2.19722)

方法2:使用(for_each)

std::cout<<"a=log(a):"<

输出:

a=log(a):
a:(1,2,3,4,5,6,7,8,9)
after a:(0,0.693147,1.09861,1.38629,1.60944,1.79176,1.94591,2.07944,2.19722)
这里,for_each的操作函数以引用方式传入,因此可以实现对自身元素的修改。 从这里可以看出,std::transform是可以实现std::for_each的功能的,也就是说std::transform是std::for_each的扩展
如两个vectorV,W要进行某个运算,如计算U = sin(V) + W,结果存入U;  
std ::vector v , w , u ;
v . push_back ( 3);
v . push_back ( 1.666);
v . push_back ( 4.5);
v . push_back ( 6.7);
w . push_back ( 3);
w . push_back ( 1.666);
w . push_back ( 4.5);
w . push_back ( 6.7);
for( autoi =v . begin (), j =w . begin (), k =u . begin (); i !=v . end (), j !=w . end (), k !=u . end ();++i ,++j ,++k )
{
    *k =sin (*i )+*j ;
}

幸好还有auto,要不然,呵呵…… transform的表达形式如下
std ::transform ( v . begin (), v . end (), w . begin (), u . begin () ,[&]( double a , double b )->double{
return sin ( a )+b ;
} );

使用std::transform可以比较方便的实现序列容器的四则运算
stl提供了plus,multiplies,divides,modulus,negate等函数对象,方便实现加减乘除等运算,具体见:http://www.cplusplus.com/reference/functional/plus/
如实现两序列的加法运算,不必写lambda表达式,直接使用stl的std::plus即可,如:
实现vector A,B,计算C = A+B
    a.clear();
    b.clear();
    c.clear();
    for(int i(1);i<10;++i)
    {
       a.push_back(i);
       b.push_back(i*10);
    }
    std::cout<<"calc c=a+b:"<());
    std::cout<<"calc c=a+b -> c:";
    printElement(c);

输出:

calc c=a+b:
a:(1,2,3,4,5,6,7,8,9)
b:(10,20,30,40,50,60,70,80,90)
calc c=a+b -> c:(11,22,33,44,55,66,77,88,99)

代码:

#include 
#include 
#include 
#include 
#include 
#include "math.h"
double g_bb = 11.2;
void foo1()
{
    auto f_add = [&](int a,int b)->int{return a+b;};
    std::cout<double{return aa+3;};//此时aa不能进行赋值操作如:aa=7;
    std::cout<double{aa = 7.0;return aa+3;};//此时aa以引用方式传入,可以进行赋值操作如:aa=7,同时修改aa的值;
    std::cout<double{aa = 8.0;g_bb = 15;return aa+3;};//此时aa可以进行赋值操作如:aa=7;,其他在作用域范围的变量都可以以引用方式调用
    std::cout<
void printElement(Container& v)
{
    std::cout<<"(";
    for(auto i = v.begin();i != v.end();++i)
    {
        std::cout<<*i;
        if(i != v.end() - 1)
            std::cout<<",";
    }
    std::cout<<")";
    std::cout< a,b,c;
    a.push_back(3);
    a.push_back(1.666);
    a.push_back(4.5);
    a.push_back(6.7);
    b.push_back(3);
    b.push_back(1.666);
    b.push_back(4.5);
    b.push_back(6.7);
    for ( std :: vector < double >:: iterator i = a . begin (); i != a . end ();++ i )
    {
        std::cout<<*i<<",";//3,1.666,4.5,6.7,
    }
    std::cout<double{return sin(i)+j;});
    printElement(c);//(3.14112,2.66147,3.52247,7.10485)

    a.clear();b.clear();c.clear();
    for(auto i(1);i<10;++i){
        a.push_back(i);
    }
    //求a的log存入c
    std::cout<<"c=log(a):"<double{return log(d);});
    std::cout<<"c:";
    printElement(c);//(0,0.693147,1.09861,1.38629,1.60944,1.79176,1.94591,2.07944,2.19722)

    b = a;
    std::cout<<"a=log(a):"<double{return log(d);});
    std::cout<<"after a:";
    printElement(a);//(0,0.693147,1.09861,1.38629,1.60944,1.79176,1.94591,2.07944,2.19722)

    a = b;
    std::cout<<"a=log(a):"<());
    std::cout<<"calc c=a+b -> c:";
    printElement(c);

    return 0;
}

写上面这篇文章,主要是因为最近经常使用序列的四则运算,如vector a + vector b,或者进行一些稍微复杂的数学运算,自己封装了一些简化的用法,截取如下:

#define INPUT
#define OUTPUT
namespace Array {
///
/// brief 加一个常数的函数对象,类似于std::plus(),不过此函数对象是用于对一个常数进行加法运算
///
template  struct plus_const : std::binary_function  {
    plus_const(const T& data){m_data = data;}
  T operator() (const T& x) const {return x+m_data;}
  T m_data;
};

///
/// brief 序列加法运算,用于序列加上单一一个值
/// param begin 序列迭代器的起始
/// param end 序列迭代器的结尾
/// param beAddData 需要进行加法运算的值
/// note 此操作会直接修改原有序列值
///
template
void add(INPUT IT begin,INPUT IT end,T beAddData)
{
    std::transform(begin,end,begin,plus_const(beAddData));
}
///
/// brief 序列加法运算,两个等长序列相加
/// param begin_addfont “加数”序列迭代器的起始
/// param end_addfont “加数”序列迭代器的结尾
/// param begin_addend “被加数”序列迭代器的起始
/// param begin_res 用于存放结果的序列的起始地址
///
template
void add(INPUT IT begin_addfont,INPUT IT end_addfont,IT begin_addend,IT begin_res)
{
    std::transform(begin_addfont,end_addfont,begin_addend,begin_res
                   ,std::plus());
}
///
/// brief 序列减法运算,两个等长序列相减
/// param begin_addfont “加数”序列迭代器的起始
/// param end_addfont “加数”序列迭代器的结尾
/// param begin_addend “被加数”序列迭代器的起始
/// param begin_res 用于存放结果的序列的起始地址
///
template
void minus(INPUT IT begin_minusfont,INPUT IT end_minusfont,IT begin_minusend,IT begin_res)
{
    std::transform(begin_minusfont,end_minusfont,begin_minusend,begin_res
                   ,std::minus());
}
///
/// brief 序列减法运算,用于序列减去单一一个值
/// param begin 序列迭代器的起始
/// param end 序列迭代器的结尾
/// param beAddData 需要进行减法运算的值
/// note 此操作会直接修改原有序列值
///
template
void minus(INPUT IT begin,INPUT IT end,T beMinusData)
{
    std::transform(begin,end,begin,plus_const(-beMinusData));
}
……
}




本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

其实在 c++语言里面const修饰的才算是一个真正的常量,在 c 语言中 const 可以说是个“冒牌货”。为什么会这样?其实是 c++ 编译器对 const 进行了加强,当 c++ 编译器遇到常量声明时,不会像 c...

关键字: c++ C语言 const

返回函数的引用去初始化一个新的引用这个和前面一样,都是不会产生副本,但是现在是用返回值去初始化一个引用声明c,也就是说这时候变成了变量temp的别名,在c的生命周期内temp是一直有效的,这样做完全可以。

关键字: c++ 返回值 引用声明

C++是一种面向对象的高级程序设计语言,是C语言的超集。

关键字: c++ C语言

c++程序员面试过程中基本上都会被问到c++11新特性吧,你是怎么回答的呢? 本文基本上涵盖了c++11的所有新特性,并有详细代码介绍其用法,对关键知识点做了深入分析,对重要的知识点我单独写了相关文章并附上了相关链接,我...

关键字: c++11

很多人谈到c++,说它特别难,可能有一部分就是因为c++的内存管理吧,不像java那样有虚拟机动态的管理内存,在程序运行过程中可能就会出现内存泄漏,然而这种问题其实都可以通过c++11引入的智能指针来解决,相反我还认为这...

关键字: c++11

c++11关于并发引入了好多好东西,这里按照如下顺序介绍: std::thread相关 std::mutex相关 std::lock相关 std::atomic相关 std::call_once相关 volatile相关...

关键字: 线程 c++11

分析:这是Adobe 公司2007 年校园招聘的最新笔试题。这道题除了考察应聘者的C++ 基本功底外,还能考察反应能力,是一道很好的题目。 在Java 中定义了关键字final ,被final 修饰的

关键字: c++ class

泛型算法中的定制操作很多算法都会比较输入序列中的元素,通过定制比较动作,可以控制算法按照编程者的意图工作。本文以string排序为例进行说明,首先是缺省的排序动作: vector v{"This","

关键字: c++

为什么是lambda?讲了这么多天的lambda表达式,有一个很基本的问题没有回答:为什么叫lambda表达式呢?首先这个lambda就是罗马字母λ,lambda表达式即λ表达式。数学上有一个概念叫λ

关键字: c++

        假设我们有个函数用来揭示处理程序的优先权,另一个函数用来在某动态分配所得的Widget 上进行某些带有优先权的处理:int priority () ; void processWi

关键字: c++ effective
关闭
关闭