是否可以在Visual C ++ 2010中将新的lambda表达式用作CLR事件处理程序?我尝试过以下代码:
SomeEvent += gcnew EventHandler(
[] (Object^ sender, EventArgs^ e) {
// code here
}
);
导致以下错误消息:
错误C3364:'System :: EventHandler':委托构造函数的参数无效;委托目标需要是指向成员函数的指针
我是在尝试不可能的,还是我的语法错了?
答案 0 :(得分:9)
以下是我的解决方案,它允许将lambdas(以及任何函数对象 - 即可以调用operator()
的任何东西)包装到委托中。它有一些限制 - 具体来说,它不支持具有跟踪引用参数的代理(C ++ / CLI中为%
,C#中为ref
/ out
;并且它对委托可以采用的参数数量有一个上限(因为VC ++ 2010不支持vararg模板) - 尽管可以通过简单的方式调整代码以支持最多可能的数量。
#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 lambdas扩展为普通类,而不是ref
或value
,因此实际应用程序受到限制。由于普通类不能包含C ++ / CLI中的托管类型(即没有对象句柄类型的成员,没有跟踪引用类型的成员,也没有value class
类型的成员),这意味着lambdas无法捕获这些类型的任何变量,或者。我没有找到跟踪引用的解决方法。对于value class
,您可以使用非托管指针(如果需要,pin_ptr
),并捕获它。
对于对象句柄,您可以将它们存储在gcroot<T>
中并捕获它 - 但是存在严重的性能影响 - 在我的测试中,通过gcroot<T>
访问成员的速度比执行它慢大约40倍使用普通对象句柄。对于单个调用来说,它实际上并不是绝对的衡量标准,但对于在循环中重复调用的东西 - 比如大多数LINQ算法 - 它将是一个杀手。但请注意,这仅适用于需要捕获lambda中的句柄的情况!如果你只是用它来编写谓词内联,或者更新一个计数器,它就可以正常工作。
答案 1 :(得分:7)
不可以,C ++ / CLI编译器没有更新以接受lambda语法。鉴于管理代码具有先机性,因此具有讽刺意味。具有讽刺意味。
答案 2 :(得分:-1)
这个页面有几个lambdas for C ++的例子:
http://msdn.microsoft.com/en-us/library/dd293608%28v=VS.100%29.aspx
Microsoft VS2010 C ++改进看起来就像实际实现C++0x lambda spec一样。因此,它们完全不受管理,属于lambda
类型。
Microsoft文档中没有任何内容暗示使用C ++ lambdas作为CLR lambdas的可能性。在这个阶段,我不得不说你不能使用C ++ lambdas作为托管委托的处理程序。