VS2010的Lambda表达式

 

 

前言:

与boost支持lambda不同的是,VS2010提供对lambda的内建支持。

 

 Lambdas

C++ 0x 中,“lambda 表达式式定并构建不具名函数象就像手写函数象一。下面是 lambda “HelloWorld”门级的示例:

 

C:/Temp>type meow.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

    cout << endl;

}

OutPut:

0 1 2 3 4 5 6 7 8 9

 

[] 操作符是 lambda 引符, 它告诉编译器一个 lambda 表达式始了。 (int n) lambda 参数声明,它告诉编译器不具名函数的函数用操作符有哪些参数, { cout << n << " "; }  合声明,它是不具名函数的函数用操作符的函数体。不具名函数的函数用操作符默返回 void

 

这样C++0x 在内部将它转换成如你在C++ 98 写的一

 

C:/Temp>type meow98.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

struct LambdaFunctor {

    void operator()(int n) const {

        cout << n << " ";

    }

};

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    for_each(v.begin(), v.end(), LambdaFunctor());

    cout << endl;

}

 

OutPut:

0 1 2 3 4 5 6 7 8 9

 

在我将不再累述不具名函数的函数用操作符默返回 void”这样“lambda 函数返回 void”法,但是 lambda 表达式做了些什是很重要的,那就是:定义类并构建

 

当然,lambda 合声明部分(函数体部分)可以包含多个声明句,譬如:

 

 

C:/Temp>type multimeow.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    for_each(v.begin(), v.end(), [](int n) {

        cout << n;

 

        if (n % 2 == 0) {

            cout << " even ";

        } else {

            cout << " odd ";

        }

    });

 

    cout << endl;

}

 

OutPut:

0 even 1 odd 2 even 3 odd 4 even 5 odd 6 even 7 odd 8 even 9 odd

 

 

lambda 函数也并不是必返回 void。如果 lambda 合声明句像是这样{ return expression; } ,那 lambda 的返回型就会自地被推断成 expression 型。

 

C:/Temp>type cubicmeow.cpp

#include <algorithm>

#include <deque>

#include <iostream>

#include <iterator>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    deque<int> d;

 

    transform(v.begin(), v.end(), front_inserter(d), [](int n) { return n * n * n; });

 

    for_each(d.begin(), d.end(), [](int n) { cout << n << " "; });

    cout << endl;

}

 

OutPut:

729 512 343 216 125 64 27 8 1 0

 

,  n * n * n 型是 int,所以 lambda 函数返回 int

有着复杂复合声明句的 lambda 函数不会自推断返回型,你必须显式指定返回型。

 

C:/Temp>type returnmeow.cpp

#include <algorithm>

#include <deque>

#include <iostream>

#include <iterator>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    deque<double> d;

 

    transform(v.begin(), v.end(), front_inserter(d), [](int n) -> double {

        if (n % 2 == 0) {

            return n * n * n;

        } else {

            return n / 2.0;

        }

    });

 

    for_each(d.begin(), d.end(), [](double x) { cout << x << " "; });

    cout << endl;

}

 

OutPut:

4.5 512 3.5 216 2.5 64 1.5 8 0.5 0

 

 -> double 是可lambda 返回型从句。它不放在左注:返回型一般在函数左声明),就像程序一直以来在C函数中做的那?因 lambda 引符 [] 就不会第一个出了,而正是它告诉编译器一个 lambda 函数始了。(核心工作最擅解决这样问题尝试C++ 中一个定的概念是否是可被解析的会疼。)

 

如果忘了指定 lambda返回型从句,编译器就会抱怨一个返回句:

 

C:/Temp>cl /EHsc /nologo /W4 borkedreturnmeow.cpp

borkedreturnmeow.cpp

borkedreturnmeow.cpp(20) : error C3499: a lambda that has been specified to have a void return type cannot return a value

borkedreturnmeow.cpp(22) : error C3499: a lambda that has been specified to have a void return type cannot return a value

 

到目前止我所介 lambda 都是无状的:它不包含数据成。你也可以有有状 lambda是通传递(原文用加引号的 capturing ,在里我翻传递似乎不太妥,故我都加括号引用原文,下同)局部量来实现的。空的 lambda 引符 [] 意味着一个无状 lambda”,但在 lambda 引符 [] 中你可以指定 capture-list

 

C:/Temp>type capturekittybyvalue.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    int x = 0;

    int y = 0;

 

    // op>>() leaves newlines on the input stream,

    // which can be extremely confusing. I recommend

    // avoiding it, and instead using non-member

    // getline(cin, str) to read whole lines and

    // then parse them. But in the interests of

    // brevity, I'll use evil op>>():

 

    cout << "Input: ";

    cin >> x >> y;

 

    v.erase(remove_if(v.begin(), v.end(), [x, y](int n) { return x < n && n < y; }), v.end());

 

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

    cout << endl;

}

 

 

Input: 4 7

OutPut:

0 1 2 3 4 7 8 9

 

如果你忘capture-list编译器就会抱怨:

 

C:/Temp>cl /EHsc /nologo /W4 borkedcapturekittybyvalue.cpp

borkedcapturekittybyvalue.cpp

borkedcapturekittybyvalue.cpp(27) : error C3493: 'x' cannot be implicitly captured as no default capture mode has been specified

borkedcapturekittybyvalue.cpp(27) : error C3493: 'y' cannot be implicitly captured as no default capture mode has been specified

 

(我很快就会解传递capture))

 

着,lambda 表达式式地定了一个不具名函数合声明 { return x < n && n < y; } 中被当作函数用操作符的函数体。然从构上看合声明句是在 main() 之内,但在概念上它是在 main() 之外的,因此如果不传递capture)到 lambda 中去,就不能在其中使用来自main() 中的局部量。

 

上面的代在内部被翻成:

 

C:/Temp>type capturekittybyvalue98.cpp

#include <algorithm>

#include <iostream>

#include <iterator>

#include <ostream>

#include <vector>

using namespace std;

 

class LambdaFunctor {

public:

    LambdaFunctor(int a, int b) : m_a(a), m_b(b) { }

 

    bool operator()(int n) const { return m_a < n && n < m_b; }

 

private:

    int m_a;

    int m_b;

};

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    int x = 0;

    int y = 0;

 

    cout << "Input: ";

    cin >> x >> y; // EVIL!

 

    v.erase(remove_if(v.begin(), v.end(), LambdaFunctor(x, y)), v.end());

 

    copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));

    cout << endl;

}

 

 

Input: 4 7

OutPut:

0 1 2 3 4 7 8 9

 

里你可以清楚 地看到是传递captures)的。函数象存了局部量的拷就使得函数象可以比通过传递capture)来建它的局部量有 更的生命期。但是,要注意:(a)在 lambda 中不能修改通过传递capture得的拷,因情况下函数用操作符是 const 属性的,(b)一些象的拷贝开销是昂的,(c)局部量的更新不会反到通过传递capture得的拷(在语义上它是原始)。很快我就会 解如有需要应该如何来理以上情况。

 

但是首先,你可以值传递capture)任何西,而不用特指明一个你想要传递(capture)的局部量。其法是使用这种形式的 lambda 引符 [=] (默认传递capture-default应该可以你想起赋值或者拷初始化 Foo foo = bar; 里的拷贝实际上是直接初始化(通初始化列表赋值),就像上面的 m_a(a))。

 

C:/Temp>type defaultcapturekittybyvalue.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    int x = 0;

    int y = 0;

 

    cout << "Input: ";

    cin >> x >> y; // EVIL!

 

    v.erase(remove_if(v.begin(), v.end(), [=](int n) { return x < n && n < y; }), v.end());

 

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

    cout << endl;

}

 

 

Input: 4 7

OutPut:

0 1 2 3 4 7 8 9

 

编译器看到 lambda 中的 x y, 就会从 main() 中按值传递capture)。

 

情形(a)要修改通过传递capture得拷贝该呢?默情况下,一个 lambda 函数用操作符是 const 属性的,但是可以通使用 mutable 把它 non-const

 

C:/Temp>type capturekittybymutablevalue.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    int x = 1;

    int y = 1;

 

    for_each(v.begin(), v.end(), [=](int& r) mutable {

        const int old = r;

 

        r *= x * y;

 

        x = y;

        y = old;

    });

 

 

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

    cout << endl;

 

    cout << x << ", " << y << endl;

}

 

OutPut:

0 0 0 6 24 60 120 210 336 504

1, 1

 

里是依次将 v 中前两个元素相乘。(我得承真的很想出一个不用 partial_sum()  adjacent_difference() 表示的例子,partial_sum() 是与前面所有的元素相乘,adjacent_difference()是与前一个元素相乘)。注意到情形(d),过传递获得的拷的更新操作并没有影 响局部量(再一次,原始值语义)。

 

如果你想理情形(b),(c)和(d):避免拷,在 lambda 察局部量的更新,以及在 lambda 中修改局部量又做呢?在这种情况下,你会想通引用传递capture by reference)。其法是这种形式的 lambda 引符 [&x, &y] (你可以把它想象成  X& x, Y& y ; 那是引用而不是取址)。

 

C:/Temp>type capturekittybyreference.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    int x = 1;

    int y = 1;

 

    for_each(v.begin(), v.end(), [&x, &y](int& r) {

        const int old = r;

 

        r *= x * y;

 

        x = y;

        y = old;

    });

 

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

    cout << endl;

 

    cout << x << ", " << y << endl;

}

OutPut:

0 0 0 6 24 60 120 210 336 504

8, 9

 

注意与 capturekittybymutablevalue.cpp 的区:(1),lambda 引符是 [&x, &y] ,(2)没有 mutable,(3),局部 x y 最后的 8 9,反了在 lambda 的修改。

 

上面的代在内部被翻成:

 

C:/Temp>type capturekittybyreference98.cpp

#include <algorithm>

#include <iostream>

#include <iterator>

#include <ostream>

#include <vector>

using namespace std;

 

#pragma warning(push)

#pragma warning(disable: 4512) // assignment operator could not be generated

 

class LambdaFunctor {

public:

    LambdaFunctor(int& a, int& b) : m_a(a), m_b(b) { }

 

    void operator()(int& r) const {

        const int old = r;

 

        r *= m_a * m_b;

 

        m_a = m_b;

        m_b = old;

    }

 

private:

    int& m_a;

    int& m_b;

};

 

#pragma warning(pop)

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    int x = 1;

    int y = 1;

 

    for_each(v.begin(), v.end(), LambdaFunctor(x, y));

 

    copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));

    cout << endl;

 

    cout << x << ", " << y << endl;

}

 

原文地址:http://www.cppblog.com/kesalin

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值