有没有办法以适用于 C++/CLI 托管类型的方式应用 function-signature-as-template-parameter 解包习惯用法?
例如,考虑以下代码:
#include <msclr/gcroot.h>
using namespace System;
template<typename... Args>
ref struct ManagedDelegate abstract
{
delegate void Fn(Args...);
};
template<typename Signature>
struct Method;
template<typename... Args>
struct Method<void(Args...)>
{
using Fn = typename ManagedDelegate<Args...>::Fn;
Method(Fn^ m) : m_Method(m) {}
void operator()(Args... args)
{
auto method = safe_cast<Fn^>(m_Method);
method(args...);
}
private:
msclr::gcroot<Fn^> m_Method;
};
void f1(int a, int b)
{
Console::WriteLine("a = {0}, b = {1}", a, b);
}
void f2(String^ s)
{
Console::WriteLine("s = {0}", s);
}
int main(array<String ^> ^args)
{
using Method1 = Method<void(int, int)>;
Method1 m1(gcnew Method1::Fn(&f1));
m1(4, 5);
using Method2 = Method<void(String^)>;
Method2 m2(gcnew Method2::Fn(&f2));
m2("hello world");
return 0;
}
(单独的
ManagedDelegate
有点烦人,但遗憾的是,无法在本地类中声明委托(delegate)类型。)如果您注释掉所有
Method2
代码在底部,然后编译并按照您的预期运行——它调用 f1(4, 5)
并相应地打印。但是,尝试对托管类型参数执行相同操作会导致模板无法匹配特化并导致:
error C2027: use of undefined type 'Method<void (System::String ^)>'
这是一个编译器错误,还是有什么方法可以让它工作?为了在我的真实代码中工作,我确实需要遵守一些限制:
Method
必须是包含 gcroot
的非托管类型委托(delegate)类型。 std::forward
也是有意的,因为这也会扰乱托管类型。 (而且我也不打算传递本地引用参数,所以没有必要。)delegate void Method1Delegate(int, int);
...
Method<Method1Delegate> m1(gcnew Method1Delegate(&f1));
Args...
参数列表(operator()
和其他原因)。而且我认为不可能从托管委托(delegate)类型中提取它。 operator()
继续使用Args...
来自 Method
类型,以便它不会接受“错误”的参数。 (我确实有一个旧版本的代码,它直接在 Args
上模板化了 operator()
,但这给 IntelliSense 一种错误的印象,即它可以接受任何参数。) void
.我知道如何用上面的代码做到这一点——只要可能的话,任何重写都不应该阻止它的工作。 编辑:作为托管参数在可变参数中工作的演示,可以添加:
template<>
struct Method<void(String^)>
{
using Fn = typename ManagedDelegate<String^>::Fn;
Method(Fn^ m) : m_Method(m) {}
template<typename... Args>
void operator()(Args... args)
{
auto method = safe_cast<Fn^>(m_Method);
method(args...);
}
private:
msclr::gcroot<Fn^> m_Method;
};
如果调用更改为
m2(gcnew String("hello world"));
,则此方法有效。强制使用正确的类型,或 operator()
更改为接受单个 String^
参数而不是开放的可变参数。所以问题肯定在于匹配可变参数模板特化,而不是其他地方。
最佳答案
我可以通过放弃函数签名特化并单独指定签名组件来做我想做的事:
template<typename R, typename... Args>
ref struct ManagedDelegate abstract
{
delegate R Fn(Args...);
};
template<typename R, typename... Args>
struct Method
{
using Fn = typename ManagedDelegate<R, Args...>::Fn;
Method(Fn^ m) : m_Method(m) {}
R operator()(Args... args)
{
auto method = safe_cast<Fn^>(m_Method);
return method(args...);
}
private:
msclr::gcroot<Fn^> m_Method;
};
//...
using Method2 = Method<void, String^>;
Method2 m2(gcnew Method2::Fn(&f2));
m2("hello world");
这并不理想,但它确实可以编译和工作。但是,我仍然对任何支持解压缩函数签名类型的替代答案感兴趣。 (我将原始问题提交为 compiler bug 。)
关于c++ - C++/CLI 中的模板函数签名解包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59676151/