C++0x引進了Lambda Expression
,這功能其實也不新鮮了,在其他語言都有類似的觀念。 為了處理Callback function,做了很多的改進 同樣的,我們從C語言時期看起。
C時期 C語言只有一種方式處理callback function,function pointer 。 像這個樣子
1 2 3 4 5 6 int add2 (int a, int b) { return a + b; }int operate (int a, int b, op2 func) { return func(a, b); } operate(3 , 2 , add2);
Function Pointer的方式處理Callback function非常經典,幾乎可以在所有純C語言開發的程式碼看到他。不過還是有可以改進的地方,例如
速度: 由於傳進來的是指標,只能間接呼叫,無法有足夠的資訊做最佳化。
狀態: Function比較難保存運行中的特定狀態。
C++98時期 C++的Functor(仿凾式)就是因此而生的,透過overload operator()
,來改進上述所無法缺憾。搭配template的使用,使其應用更具彈性。可以同時支援Callback function跟Functor兩種形式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 struct Add2 { int operator () (int a, int b) { return a + b; } }; int sub2 (int a, int b) { return a - b; }template <typename T>int operate (int a, int b, T callback) { return callback(a, b); } operate(3 , 2 , Add2()); operate(3 , 2 , sub2);
之後有人想要將class member method跟static class method也包裝成Functor的形式,有Loki
跟Boost
兩大主流為主。這裡就不多談了,在C++0x中也將boost的bind跟function列入TR1裡,可以很方便的產生Functor。
C++0x時期 Functor雖好,不過每要新增一個新功能,就要寫一個function object,如果是只被使用一次的functor,維護這段程式碼需要付出成本, 因此就產生了一個暱名函數(anonymous function),像這樣。
1 2 3 4 5 6 7 8 for_each(v.begin (), v.end (), [](int n) { cout << n; if (n % 2 == 0 ) { cout << " even " ; } else { cout << " odd " ; } });
詳細的語言規範可以參考MSDN的資料 。透過跟程式碼上下文互動,達到非常有趣的效果。
Lambda Trick 之前我們介紹過unique_ptr,用來管理動態記憶體,非memory的Resource該怎麼處理,此時就是ScopeGuard登場的時候
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class ScopeGuard { public : explicit ScopeGuard (std ::function<void ()> onExitScope) : onExitScope_(onExitScope), dismissed_(false) { } ~ScopeGuard() { if (!dismissed_) { onExitScope_(); } } void Dismiss () { dismissed_ = true ; } private : std ::function<void ()> onExitScope_; bool dismissed_; private : ScopeGuard(ScopeGuard const &); ScopeGuard& operator =(ScopeGuard const &); }; int main () { HANDLE h = CreateFile(...); ScopeGuard onExit ([&] { CloseHandle(h); }) ; return 0 ; }
或者將不同function signature,由於要經過相同的處理流程,於是用lambda expression隱藏了細節,以下是DirectX ToolKit 的部分程式碼。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 template <typename T, typename TCreateFunc>static T* DemandCreate (Microsoft::WRL::ComPtr<T>& comPtr, TCreateFunc createFunc) { T* result = comPtr.Get(); if (!result) { ThrowIfFailed( createFunc(&result) ); comPtr.Attach(result); } return result; } ID3D11SamplerState* CommonStates::LinearClamp () const { return DemandCreate(pImpl->linearClamp, [&](ID3D11SamplerState** pResult) { return pImpl->CreateSamplerState(D3D11_FILTER_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_CLAMP, pResult); }); } ID3D11DepthStencilState* CommonStates::DepthRead () const { return DemandCreate(pImpl->depthRead, [&](ID3D11DepthStencilState** pResult) { return pImpl->CreateDepthStencilState(true , false , pResult); }); }
可以看得出來,DemandCreate處理的流程相同,但是船進去的type跟Callback function不同,就能達到一般化的效果,這也是我目前看到最有趣的例子。