標籤:
什麼是Lambda?
C++ 11加入了一個非常重要的特性——Lambda運算式。營裡(戴維營)的兄弟都對Objective-C很熟悉,許多人多block情有獨鐘,將各種回呼函數、代理通通都用它來實現。甚至有人選擇用FBKVOController、BlocksKit等開源架構將KVO、控制項事件處理都改為通過block解決。原因就是簡單、方便、直觀,函數的定義和使用出現在同一個地方。這裡的Lambda運算式實際上和block非常類似,當然如果你用它和Swift語言的閉包比較,那就是一回事了。下面先看幾個Lambda和block的範例程式碼。
1 .Objective-C的block範例程式碼,使用^表示block類型,總體來說與函數指標的定義類似。
#import <Foundation/Foundation.h>int main(){ void (^block)() = ^void() { NSLog(@"In block"); }; block(); return 0;}
編譯運行:
$ clang main.m -framework Foundation$ ./a.out 2015-01-28 14:17:52.763 a.out[9901:165707] In block
2 .Swift的閉包,參數列表、傳回值類型等都寫在花括弧{}內部。
let closure = { () -> Void in println("In swift")}closure()
編譯運行:
$ swiftc main.swift $ ./main In swift
測試Swift也可以直接使用Playground或REPL(Read-Eval-Print-Loop)環境。
3 .C++的Lambda運算式。
#include <iostream>int main(){ auto lambda = []() -> void{ std::cout << "In lambda" << std::endl; }; lambda(); return 0;}
編譯運行,注意使用C++ 11的特性進行編譯:
$ clang++ main.cpp -std=c++11$ ./a.out In lambda
Lambda、block實際上都是一個閉包(closure),它們都類似於一個匿名的函數,但是擁有捕獲所在範圍中變數的能力;能夠將函數做為對象一樣使用。通常用它們來實現回呼函數、代理等功能。
Lambda使用方法
1 .基本形式
| 文法 |
序號 |
[ 捕獲列表 ] ( 形參數列表 ) mutable(可選) 異常屬性 -> 傳回值類型 { 函數體 } |
(1) |
[ capture-list ] ( params ) -> ret { body } |
(2) |
[ capture-list ] ( params ) { body } |
(3) |
[ capture-list ] { body } |
(4) |
- (1)為完整的形式,包含變數捕獲列表、形參列表、可變屬性(可選)和傳回值類型等。
- (2)省略了mutable,表示Lambda不能修改捕獲的變數。
- Lambda的傳回值類型如果可以由函數體中的實際傳回值推匯出,可以省略。
- 如果沒有形參,可以省略圓括弧。
2 .捕獲列表
lambda運算式中可以擷取(捕獲)它所在範圍中的變數值,並且有兩種捕獲方式:引用和值。我們可以在捕獲列表中設定各變數的捕獲方式。如果沒有設定捕獲列表,lambda預設不能捕獲任何的變數,這點與block不同。
#include <iostream>int main(){ int a = 123; auto lambda = []()->void{ std::cout << "In lambda: " << a << std::endl; }; lambda(); return 0;}
編譯運行:
$ clang++ main.cpp -std=c++11main.cpp:8:33: error: variable ‘a‘ cannot be implicitly captured in a lambda with no capture-default specified std::cout << "In lambda: " << a << std::endl; ^main.cpp:5:6: note: ‘a‘ declared here int a = 123; ^main.cpp:7:16: note: lambda expression begins here auto lambda = []()->void{ ^1 error generated.
在[]中設定捕獲列表,就可以在lambda中使用變數a了,這裡使用按值(=, by value)捕獲。
#include <iostream>int main(){ int a = 123; auto lambda = [=]()->void{ std::cout << "In lambda: " << a << std::endl; }; lambda(); return 0;}
編譯運行:
$ clang++ main.cpp -std=c++11$ ./a.out In lambda: 123
捕獲列表的設定方式:
|設定方式|結果| |-|-| |[]|不捕獲| |[=]|全部按值捕獲| |[&]|全部按引用捕獲| |[a, b]|按值捕獲變數a和b| |[&a, b]|按引用捕獲a,按值捕獲b| |[&, a]|按值捕獲a,其它變數全部按引用捕獲| |[=, &a]|按引用捕獲a,其它全部按值捕獲|
注意: 捕獲列表沒有先後順序;捕獲列表中的變數不能出現重複申明,比如[&, &a]在&中已經包含了&a的申明了,因此不能再出現&a。
具體的捕獲類型,可以通過列印變數地址進行查看。
auto lambda = [=]()->void{ std::cout << "In lambda: " << &a << std::endl;};
運行結果為:
$ clang++ main.cpp -std=c++11$ ./a.out 0x7fff555c69b8In lambda: 0x7fff555c69b0
auto lambda = [&]()->void{ std::cout << "In lambda: " << &a << std::endl;};
運行結果為:
$ clang++ main.cpp -std=c++11$ ./a.out 0x7fff58a9b9b8In lambda: 0x7fff58a9b9b8
3 .可變類型(mutable)
按值傳遞到lambda中的變數,預設是不可變的(immutable),如果需要在lambda中進行修改的話,需要在形參列表後添加mutable關鍵字(按值傳遞無法改變lambda外變數的值)。
#include <iostream>int main(){ int a = 123; std::cout << a << std::endl; auto lambda = [=]() mutable ->void{ a = 234; std::cout << "In lambda: " << a << std::endl; }; lambda(); std::cout << a << std::endl; return 0;}
編譯運行結果為:
$ clang++ main.cpp -std=c++11lishan:c_study apple$ ./a.out 123In lambda: 234 #可以修改123 #注意這裡的值,並沒有改變
如果沒有添加mutable,則編譯出錯:
$ clang++ main.cpp -std=c++11main.cpp:9:5: error: cannot assign to a variable captured by copy in a non-mutable lambda a = 234; ~ ^1 error generated.
4 .傳回值類型。
lambda的傳回值類型可以省略,編譯器會根據實際傳回值的類型自動推導。
#include <iostream>int main(){ int a = 123; std::cout << a << std::endl; auto lambda = [=]() mutable{ a = 234; std::cout << "In lambda: " << a << std::endl; return a; }; int b = lambda(); std::cout << b << std::endl; return 0;}
編譯運行:
$ clang++ main.cpp -std=c++11$ ./a.out 123In lambda: 234234
參考資料
- https://msdn.microsoft.com/en-us/library/dd293603.aspx
- http://www.oracle.com/technetwork/articles/servers-storage-dev/howto-use-lambda-exp-cpp11-2189895.html
- http://en.cppreference.com/w/cpp/language/lambda
- http://www.cprogramming.com/c++11/c++11-lambda-closures.html
本文檔由長沙戴維營教育整理。
C++教程之lambda運算式一