lambda表達式是函數式編程的基礎。咱對於函數式編程也沒有足夠的理解,因此這里不敢胡言亂語,有興趣的可以自己查找相關資料看下。這里只是介紹C++11中的lambda表達式以及與此相關的閉包(closure)。
同樣,這里首先給出參考文檔
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2927.pdf
其次,給出兩個例子,可以看出lambda表達式的寫法
[](int x, int y) { return x + y; }
[](int x, int y) -> int { int z = x + y; return z + x; } //表明返回值類型是int型
也就是說,一個lambda表達式很類似於普通的函數定義的寫法,區別在於三點,一是沒有函數名(你也可以認為函數名是[]),二是這個函數沒有普通函數那樣的返回值類型,返回值類型的寫法在第2行,即在參數列表與函數定義之間以箭頭寫明,三是在參數列表之前有一個[] (其實這里還可能有更多的形式,后面會說)
第三,lambda表達式的標准形式是什么?
如下。這里先只是列出,后面會詳細解釋。
lambda-expression:
lambda-introducer lambda-declaratoropt compound-statement
lambda-introducer:
[ lambda-captureopt ]
lambda-capture:
capture-default
capture-list
capture-default , capture-list
capture-default:
&
=
capture-list:
capture
capture-list , capture
capture:
identifier
& identifier
this
lambda-declarator:
( parameter-declaration-clause ) attribute-specifieropt mutableopt exception-specificationopt trailing-return-typeopt
第四,進一步的分析
上面的標准形式中,最不好理解的可能就是lambda-capture了。其實它對應的就是閉包的概念,而作用是描述外部傳入的參數。
wiki上有一個例子,如下
std::vector<int> someList;
int total = 0;
std::for_each(someList.begin(), someList.end(), [&total](int x) {
total += x;
});
std::cout << total;
這段代碼做的事情是把someList中的所有變量求和。其中定義的lambda函數如下,
[&total](int x) {total += x;}
與前面的例子不同,這里的lambda-introducer是[&total]而不是之前的[]。這么寫的作用是,在這個lambda函數中以引用方式使用外部變量total,這樣求和結果就可以存放於這個變量中。那么類似的,還可以以傳值方式使用外部變量,寫成[total]就可以,而且,如果需要用到的外部變量較多,可以有簡略的寫法。一個完整的列表如下(來自wiki,這里我把他們翻譯成我們比較習慣的表述形式)
[] // 沒有定義任何變量。使用未定義變量會導致錯誤。
[x, &y] // x 以傳值方式傳入(默認),y 以引用方式傳入。
[&] // 任何被使用到的外部變量皆隱式地以引用方式加以使用。
[=] // 任何被使用到的外部變量皆隱式地以傳值方式加以使用。
[&, x] // x 顯示地以傳值方式加以使用。其余變量以引用方式加以使用。
[=, &z] // z 顯示地以引用方式加以使用。其余變量以傳值方式加以使用。
第五,代碼練習及驗證
接下來,我們自己寫一些代碼來做一些練習以及驗證一些事情。以下代碼在visual studio 2010下測試通過。
首先是模仿實例寫一些代碼。
#include <iostream>
#include <algorithm>
#include <vector>
void ShowVector(std::vector<int>& vecTest)
{
std::for_each(vecTest.begin(), vecTest.end(),
[](int x)
{
std::cout<<x<<' ';
}
);
std::cout<<std::endl;
}
int main()
{
std::vector<int> vecTest(10,1);
ShowVector(vecTest);
std::for_each(vecTest.begin(), vecTest.end(),
[](int& x)
{
x += 2;
}
);
ShowVector(vecTest);
int iTotal = 0;
std::for_each(vecTest.begin(), vecTest.end(),
[&iTotal](int x)
{
iTotal += x;
}
);
std::cout << std::endl << iTotal << std::endl;
iTotal = 0;
std::for_each(vecTest.begin(), vecTest.end(),
[&](int x)
{
iTotal += x;
}
);
std::cout << std::endl << iTotal << std::endl;
//iTotal = 0;
//std::for_each(vecTest.begin(), vecTest.end(),
// [=](int x)
// {
// iTotal += x; // build error,error C3491: 'iTotal': a by-value capture cannot be modified in a non-mutable lambda
// }
//);
//std::cout << std::endl << iTotal << std::endl;
//iTotal = 0;
//std::for_each(vecTest.begin(), vecTest.end(),
// [iTotal](int x)
// {
// iTotal += x; // build error, error C3491: 'iTotal': a by-value capture cannot be modified in a non-mutable lambda
// }
//);
//std::cout << std::endl << iTotal << std::endl;
iTotal = 0;
std::for_each(vecTest.begin(), vecTest.end(),
[=, &iTotal](int x)
{
iTotal += x;
}
);
std::cout << std::endl << iTotal << std::endl;
int iAdd = 1;
std::for_each(vecTest.begin(), vecTest.end(),
[=](int& x)
{
x += iAdd;
}
);
ShowVector(vecTest);
std::for_each(vecTest.begin(), vecTest.end(),
[iAdd](int& x)
{
x += iAdd;
}
);
ShowVector(vecTest);
char e = 0;
scanf_s("%c", &e, 1);
return 0;
}
運行結果如下,
1 1 1 1 1 1 1 1 1 1
3 3 3 3 3 3 3 3 3 3
30
30
30
4 4 4 4 4 4 4 4 4 4
5 5 5 5 5 5 5 5 5 5
這里值得注意的是其中的注釋掉的代碼,這里我們以傳值方式使用外部變量,但是在lambda函數內部卻試圖修改該變量的值,其實這是沒什么作用的,而visual studio直接給出報錯,檢查很仔細,不錯。
那么我們再考慮一個問題,lambda表達式的運行效率?我們來試一下。測試機器雙核4G內存。
#include "windows.h"
#include <iostream>
#include <algorithm>
#include <vector>
void ModVal(int& iVal)
{
iVal *= 3;
}
#define ARRAY_SIZE 10000000
int main()
{
int m = 0;
std::vector<int> vecTest(ARRAY_SIZE,0);
std::for_each(vecTest.begin(), vecTest.end(),
[&m](int& x)
{
x = m++;
}
);
// test1
//std::for_each(vecTest.begin(), vecTest.end(),
// [](int& x)
// {
// x *= 3;
// }
//);
// test2
//for (int i = 0; i < ARRAY_SIZE; i++)
//{
// vecTest[i] *= 3;
//}
// test3
std::for_each(vecTest.begin(), vecTest.end(), ModVal);
char e = 0;
scanf_s("%c", &e, 1);
return 0;
}
分別對以上的test1, test2和test3使用QueryPerformanceFrequency()函數計時,我們可以得到如下的結果,
test1: 26.8ms
test2: 27.6ms
test3: 24.2ms
也就是說,最快的是test3,即不采用lambda表達式,而是直接用函數……多少有點出乎意料。而最慢的是直接用for循環的操作,不過它和用lambda表達式的結果很接近。
第六,其他
成員函數內部的lambda函數,lambda函數作為對象。留着后面寫。