論壇首頁
-> IT人生
ZT:C++ Lambda 表達式使用詳解 (發表於10年前)
閱讀上一個主題 :: 閱讀下一個主題
作者
正文
webdriver (只看此人 )
時間: 2014-10-20 17:17
C++ 11 對LB的支持,對於喜歡Functional Programming的人來說,無疑是超好消息。它使得C++進入了和C#,JavaScript等現代流行的程序設計語言所代表的名人堂。
不熟悉LB本身的網友,可以看MSDN文章
( msdn.microsoft.com/en-...93608.aspx ),我僅僅簡單地分析一下VC++中LB的用法,實現,和性能。
無名引用
對於一次性的,帶參數表達式,用LB可以節省不必要的class定義和維護,簡化程序的設計-維護代價。
比如下面的vector處理代碼,簡潔明了:
代碼:
vector<int> v1(10, 1);
int sum = 0;
for_each (v1.begin(), v1.end(), [&](int i){ sum += i; });//Line1
否則,我們必須定義一個function類,把如此簡單的事情復雜化。用了LB,我們把定義function 類的工作,轉交給編譯。VC++中,上述LB編譯的實現是產生一個隱身類:
代碼:
class _lambda_a01 {
int &capture1_;
public:
_lambda_a01(int &x): capture1_(x) {} //Line2
operator void (int i) { capture1_ += I; }
};
在引用時(Line1),它變成:
代碼:
_lambda_a01 lbd1(sum);
for(auto a:v1){
ldb1(a);
}
讀者也許好奇,為什麼C++不直接把LB轉換成inline expression (inline 表達式),而是要生成一個隱身類呢?這是因為LB的確可以當成“type”變量來用,這樣使得LB和其他類有了同等地位。比如:
代碼:
vector<int> v1(10, 1);
int sum = 0;
for_each (v1.begin(), v1.end(), [&](int i){ sum += i; });//Line1
vector<int> v2(10, 1);
int sum2 = 0;
for_each (v1.begin(), v1.end(), [&](int i){ sum2 += i; });//Line2
我們如果用上述的方法,Line1和Line2重復代碼,是軟件工程的大忌。我們可以用下列LB使用模式:
有名無型引用
代碼:
vector<int> v1(10, 1);
vector<int> v2(10, 1);
int sum = 0;
auto lb = [&](int i){ sum += i; }; //Line0
for_each (v1.begin(), v1.end(), lb);//Line1
sum = 0; // Line1.1
for_each (v1.begin(), v1.end(), lb});//Line2
在Line0,我們定義了一個有名(lb)無型的LB,可以在Line1和Line2重復使用。
注意的是,
1) 每個LB的“定義”都會產生新的“隱身”類,所以盡量用“有名引用”,會減少代碼的size,縮小工作集。
2) 定義時,LB一次性“俘獲”環境變量,所以上面修改後的代碼加了Line1.1,以便正確表達應用邏輯。
3) 俘獲可以是“傳值(by value)”也可以是“傳引用(by reference)。我們Line0用的是by reference.
有名有型引用
上面兩種LB使用模式,是LB應用的主要模式,它直接反映出了LB的優點。另一方面說,既然LB無非是隱身類,我們沒有理由不能把它當作普通變量使用。這個模式是一種簡化的functor使用模式。我們可以把LB定義成一個std::function,比如上面的auto lb可以定義成:
代碼:
std::function <void(int)> lb; //lb is a function which takes an integer and returns void
注意到用這個定義,使得我們可以推遲給LB變量賦值,甚至一變量賦多址(不同時間)。下面就是一個簡單用例:
代碼:
struct MyLambda
{
std::function <int (int)> _lbda;//line1
int _extra;
};
MyLambda TestLambdaObj(int t)
{
MyLambda ret;
if (t == 1)
{
ret._extra = t;
ret._lbda = [=](int x) -> int { return t + x; }; //line2
return ret;
}
else
{
ret._extra = t;
ret._lbda = [=](int x) -> int { return t * x; };//line3
return ret;
}
}
void TestLambdaFun2(int t)
{
MyLambda ret = TestLambdaObj(t);
int v = ret._lbda(t); //line4
printf(“v is ‘%d’ for type %d”, v, t);
}
我們先定義MyLambda數據類,並與其定義了一了function成員_lbda,根據C++ SPEC,他可以由LB轉換構造,並且和普通的類變量無甚區別。然後我們可以運行時給它賦值(line2,line3), 當作普通function來使用(line4)。
注意的是:
function的定義中沒有“閉包”的概念,閉包的形成是在LB創建時實現(line2,line3)。
把LB賦值給function變量,必然造成調用時(line4)的間接性(通過函數指針),其性能相當於虛擬函數,也不能inline化,當然比直接調用有所下降。
閉包(closure)是LB的獨特附加值
如果你問為什用LB而不用std::function?我的回答是“閉包”。
C++用LB來實現閉包,是一個簡化繁瑣的class初始化的syntax sugar。這一點是std::function所不可替代的。比如說:
代碼:
auto sum = 0;
auto step = 2;
auto lb = [&](int i){ sum += i + step; }//capture sum and step by ref
lb形成自己的閉包,自動從環境中俘獲了sum和step,若用class實現,上面的程序起碼增加10行代碼。
_________________There is no wisdom tree; nor a stand of a mirror bright, Since all is void, where can the dust alight?
樓主 |
電梯直達
webdriver (只看此人 )
時間: 2014-10-20 17:17
LB性能初探
下面的簡單程序,測試四種功能完全一樣,但使用不同表達式的邏輯:
1)t =1 時用LB,
2)t=2 時用直接表達式
3)t=3 時用函數
4)t=4時用std::function間接調用LB
代碼:
void TestLambdaFun(int t)
{
using namespace std;
vector<int> v1(10, 1);
int x = 0;
int u = 0;
if (t == 1)
{
clock_t begin = clock();
for (int i = 0; i < 100000; ++i)
{
for_each (v1.begin(),
v1.end(),
[&x, &u](int i){ u += i+(x++); });// Line 1
}
clock_t end = clock();
auto spent = double(end – begin) / CLOCKS_PER_SEC;
printf(“spent for type ‘%d’ is %f u is %d\n”, t, spent, u);
}
else if (t == 2)
{
clock_t begin = clock();
for (int i = 0; i < 100000; ++i)
{
auto _First = v1.begin();
auto _Last = v1.end();
for (; _First != _Last; ++_First)
{
u = *_First+(x++); // Line 2
}
}
clock_t end = clock();
auto spent = double(end – begin) / CLOCKS_PER_SEC;
printf(“spent for type ‘%d’ is %f u is %d\n”, t, spent, u);
}
else if (t == 3)
{
clock_t begin = clock();
for (int i = 0; i < 100000; ++i)
{
auto _First = v1.begin();
auto _Last = v1.end();
for (; _First != _Last; ++_First)
{
FuncAdd(u, x, *_First); // Line 3
}
}
clock_t end = clock();
auto spent = double(end – begin) / CLOCKS_PER_SEC;
printf(“spent for type ‘%d’ is %f u is %d\n”, t, spent, u);
}
else if (t == 4)
{
clock_t begin = clock();
std::function <void (int)> lbda;
for (int i = 0; i < 100000; ++i)
{
lbda = [&](int i){ u += i + (x++); };
for_each (v1.begin(), v1.end(), lbda); // Line 4
}
clock_t end = clock();
auto spent = double(end – begin) / CLOCKS_PER_SEC;
printf(“spent for type ‘%d’ is %f u is %d\n”, t, spent, u);
}
}
void FuncAdd(int &u, int &x, int i)
{
u = i+(x++);
}
下面是VC++ 2010中的測試結果:
在debug模式下,t=2時速度最快,這是因為t=1,t=3,t=4時都是用了函數調用,性能當然不及inline表達式。
在release模式下(選擇/Ob1優化,對inline函數進行inline擴展)
t=1和t=2速度完全一樣,比t=3時平均快3倍。當然,我們也可以把FuncAdd inline化。這裡的主要目的,是證明優化後,LB的性能和表達式完全一樣。證明C++ Lambda expression不是浪得虛名的隱身類的syntax sugar,而是名副其實的“表達式”。
t=4最慢,它和t=3類似。但是由於通過了std::function的虛擬函數表間接調用,/Ob1優化失去作用,使它不但要調用一個() operator,而且是通過“虛擬表”間接調用。所以從性能上說,把LB通過std::function間接使用,失去了LB的性能優勢。
總結
C++ 11 的lambda expression(簡稱LB),在可以保證和inline expression同樣性能的條件下,增加了參數功能和閉包功能,是我們寫出簡潔,明了,易維護代碼的絕佳工具。應用時,為了避免代碼重復和增加隱身類的數量,可用有名無型的LB變量。LB也可以賦值於std::function,當作函數指針使用,但是性能不及簡單地inline使用。
_________________There is no wisdom tree; nor a stand of a mirror bright, Since all is void, where can the dust alight?
沙發 |
返回頂端
論壇首頁
-> IT人生
所有的時間均為 美國太平洋時間
第1 頁,共1 頁
注:以上論壇所有發言僅代表發帖者個人觀點, 並不代表本站觀點或立場, 加西網對此不負任何責任。 投資理財及買房賣房版面的帖子不構成投資建議。投資有風險,責任請自負 對二手買賣中的虛假信息,買賣中的糾紛等均與本站無關。
您不能 在本論壇發表新主題 您不能 在本論壇回復主題 您不能 在本論壇編輯自己的文章 您不能 在本論壇刪除自己的文章 您不能 在本論壇發表投票 您不能 在這個論壇添加附件 您可以 在這個論壇下載文件
論壇轉跳:
選擇一個論壇
焦點板塊
加西知乎
發布求助
買房賣房
美麗的菲沙河谷
地產投資
加西菜園
商海微瀾
租房那些事兒
加國一家親
咱們本拿比
溫哥華不眠夜
日月當空
休閒Langley
菩提樹下
神愛世人
素裡新家園
白石華人
歌聲響起
讀書沙龍
單身情緣
新時代之光
歡迎來新西敏
我愛列治文
高貴林的朋友們
中英雙語班在Walton Elementary
生活互助
法律探索
醫療健康
准備養老
佛道淨土.中醫養生
天下收藏
減肥
投資理財
親子教育
豬寶寶俱樂部
汽車天地
溫村Jeep之家
錢幣交流小站 (Coin Community)
免費個人廣告
會員之家
北美盾之家
流協
吃喝與玩樂
玩玩樂樂
加西群英游艇會
結伴旅游
跳舞俱樂部
北美Hiking
吃吃喝喝
社團之窗
南粵茶居
交通大學校友會
福建同鄉在此聚集
加拿大四川同鄉聯誼會
同濟大學溫哥華校友會
精彩貼圖
精彩連環畫
開心一笑
國家與城市
新聞時評
軍事天地
教師罷工
大溫市選
情系中國
綿綿鄉音
香港占中
嶺南人家
悠悠華夏
只剛上海話
西雅圖夜未央
愛好與沙龍
原創原地
攝影交流
白石鎮攝影交流
視聽世界
瘋人院 - 你的喜鵲巢
電子電玩
Twitter 交流
蘋果樂園
android的星空
無限手機計劃
部落沖突
大數據論壇
網站建設
顛覆小組—LINUX
IT人生
iTalkBB討論組
English Corner
生活與教育
美好家居
精致生活
移民資訊
溫哥華留學
寵物樂園
致富與創業
精明消費
炫彩珠寶
南下購物
創業沙龍
職場風雲
運動與休閒
羽毛球場
高爾夫俱樂部
休閒運動
加西滑雪俱樂部
加西游泳俱樂部
武術之家
國家台球事務辦公室
加西圍棋俱樂部
網球天地
癡迷足球
乒乓球場
年青人天地
周末娛樂
美酒咖啡
彩妝
西餐美食
溫哥華高爾夫
webdriver, webdriver
最新新聞
熱門新聞
熱評新聞
無人機穿越大理千年古塔落券洞內 涉事"飛手"被拘
澤連斯基稱烏克蘭已准備好實現為期30天的停火!
妻子舉報丈夫涉嫌重婚:同一小區內兩個"家"
A股"掌門"薪酬曝光:13名董事長年薪超千萬
美英宣布達成貿易協議 但具體細節尚待敲定
緬甸多個電詐園區用"星鏈"上網 馬斯克被質疑...
中國工信部出手整頓隱藏式汽車車門把手
英媒:比黃金還珍貴的月塵從中國運抵英國
美國官員:巴基斯坦用殲-10擊落印度陣風,沒用F-16
紀念蘇聯偉大衛國戰爭勝利80周年,紀念的是什麼?
應對貿易戰,歐盟的"籌碼"與"祭品"...
反抗中共威權教會我的事:順從無法換來仁慈
喉嚨出現5症狀 可能是食道癌前兆
金巧巧回應"不適合演農村人"言論:原意是說演技窄
印巴沖突升級以來莫迪首次發表講話,敦促各部...
"疫苗導致的死亡海嘯已經來臨" 中國博主發文
塵埃落定!"安胖"牽手桑巴軍團,劍指世界杯
廣東發布31條振興消費方案 以舊換新、薪資成長…
下狠手!9場狂丟16球 國足門神被棄用?
歐冠決賽:法甲豪門被看高,藍黑軍團存隱憂
61歲韋唯定居泰國,養水牛種水稻
素裡雙重凶殺案 21歲男子被控罪
科學家"算"出地球生命終結的時間
高速路上2歲娃駕車 父親一臉淡定 網友憤怒
上海推動商戶「輕微免罰」 讓企業休養生息振經濟
中國很難介入"巴鐵"和印度之戰(圖
川普稱中國要求會談 美財長:中國不是發展中國家
"他們滿腦子都是錢,留學生千萬別來!"
瑞士談判之際,美中傳來利好大消息
中國不賣了 價漲210% "卡脖子"大殺器
"疫苗導致的死亡海嘯已經來臨" 中國博主發文
加拿大皇家馬戲團素裡列治文開演
蓋茨擬捐幾乎全部身家 斥馬斯克殺最貧窮兒童
溫市中心將聳立315米高樓 BC最高
少女濾鏡徹底碎了!75歲王薇薇紅毯生圖嚇到網民
內幕:習誤判慘遭重擊 終給川普遞上誠意清單
塵埃落定!"安胖"牽手桑巴軍團,劍指世界杯
廣東發布31條振興消費方案 以舊換新、薪資成長…
瑞士談判之際,美中傳來利好大消息
下狠手!9場狂丟16球 國足門神被棄用?
歐冠決賽:法甲豪門被看高,藍黑軍團存隱憂
61歲韋唯定居泰國,養水牛種水稻
素裡雙重凶殺案 21歲男子被控罪
上海推動商戶「輕微免罰」 讓企業休養生息振經濟
中國很難介入"巴鐵"和印度之戰(圖