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行代碼。