Visual C++ 2010 中作為 CLR (.NET) 委託/事件處理程序的 Lambda 表達式
是否可以將 Visual C++ 2010 中的新 lambda 表達式用作 CLR 事件處理程序?我試過以下程式碼:
SomeEvent += gcnew EventHandler( [] (Object^ sender, EventArgs^ e) { // code here } );它會導致以下錯誤消息:
錯誤 C3364:“System::EventHandler”:委託建構子的參數無效;委託目標需要是指向成員函式的指針
我是在嘗試不可能的事情,還是只是我的語法錯誤?
不行,C++/CLI 編譯器沒有更新以接受 lambda 語法。順便說一句,考慮到託管程式碼的領先優勢,這頗具諷刺意味。
以下是我的解決方案,它允許將 lambdas(以及任何函式對象 - 即任何
operator()可以呼叫的對象)包裝到委託中。它有一些限制 - 具體來說,它不支持帶有跟踪引用參數的委託(%在 C++/CLI 中,ref/out在 C# 中);並且它對委託可以採用的參數數量有上限(因為 VC++2010 不支持可變參數模板)——儘管可以對程式碼進行微調以支持任意數量的參數。#pragma once #include <new> #include <type_traits> namespace detail { struct return_type_helper { private: template<class D> struct dependent_false { enum { value = false }; }; template <class D> struct illegal_delegate_type { static_assert(dependent_false<D>::value, "Delegates with more than 2 parameters, or with parameters of tracking reference types (T%), are not supported."); }; struct anything { template<class T> operator T() const; }; public: template<class D> static decltype(static_cast<D^>(nullptr)()) dummy(int(*)[1]); template<class D> static decltype(static_cast<D^>(nullptr)(anything())) dummy(int(*)[2]); template<class D> static decltype(static_cast<D^>(nullptr)(anything(), anything())) dummy(int(*)[3]); template <class D> static illegal_delegate_type<D> dummy(...); }; template<class Func, class Aligner = char, bool Match = (std::tr1::alignment_of<Func>::value == std::tr1::alignment_of<Aligner>::value)> struct aligner { static_assert(Match, "Function object has unsupported alignment"); }; template<class Func, class Aligner> struct aligner<Func, Aligner, true> { typedef Aligner type; }; template<class Func> struct aligner<Func, char, false> : aligner<Func, short> { }; template<class Func> struct aligner<Func, short, false> : aligner<Func, int> { }; template<class Func> struct aligner<Func, int, false> : aligner<Func, long> { }; template<class Func> struct aligner<Func, long, false> : aligner<Func, long long> { }; template<class Func> struct aligner<Func, long long, false> : aligner<Func, double> { }; template<class Func> struct aligner<Func, double, false> : aligner<Func, void*> { }; template<class F> ref class lambda_wrapper { public: lambda_wrapper(const F& f) { pin_ptr<F> pf = (interior_ptr<F>)&f_storage; new(pf) F(f); } ~lambda_wrapper() { pin_ptr<F> pf = (interior_ptr<F>)&f_storage; pf->~F(); } template <class D> operator D^ () { D^ d = nullptr; return gcnew D(this, &lambda_wrapper<F>::invoke<decltype(return_type_helper::dummy<D>(0))>); } private: template<class T> [System::Runtime::InteropServices::StructLayout(System::Runtime::InteropServices::LayoutKind::Sequential, Size = sizeof(T))] value struct embedded_storage { private: typename aligner<T>::type dummy; }; embedded_storage<F> f_storage; template<class R> R invoke() { pin_ptr<F> pf = (interior_ptr<F>)&f_storage; return (*pf)(); } template<class R, class A1> R invoke(A1 a1) { pin_ptr<F> pf = (interior_ptr<F>)&f_storage; return (*pf)(a1); } template<class R, class A1, class A2> R invoke(A1 a1, A2 a2) { pin_ptr<F> pf = (interior_ptr<F>)&f_storage; return (*pf)(a1, a2); } }; } template<class F> detail::lambda_wrapper<F>^ make_delegate(F f) { return gcnew detail::lambda_wrapper<F>(f); }範例用法:
Func<int, String^, int>^ f2 = make_delegate([&](int x, String^ y) -> int { Console::WriteLine("Func {0} {1}", x, y); return 2; });雖然這在技術上可以滿足您的需求,但由於 C++0x lambda 被擴展為普通類,而不是
ref或類,因此實際應用受到了一定的限制value。由於普通類不能包含 C++/CLI 中的託管類型(即沒有對象句柄類型的成員、沒有跟踪引用類型的成員和沒有value class類型的成員),這意味著 lambda 也不能擷取這些類型的任何變數。我知道沒有用於跟踪參考的解決方法。對於value class,您可以獲取一個指向它的非託管指針(pin_ptr如果需要),然後擷取它。對於對象句柄,您可以將它們儲存在 中
gcroot<T>,並擷取它們——但會影響性能——在我的測試中,通過訪問成員gcroot<T>的速度比使用普通對象句柄慢大約 40 倍。實際上,對於單個呼叫來說,絕對量並不多,但對於在循環中重複呼叫的東西——比如說,大多數 LINQ 算法——這將是一個殺手。但請注意,這只適用於需要在 lambda 中擷取句柄的情況!如果你只是用它來寫一個謂詞內聯,或者更新一個計數器,它會工作得很好。