c++ - C++/CLI 中的模板函数签名解包

标签 c++ visual-studio-2017 c++-cli c++17

有没有办法以适用于 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)类型,如此处所示,但在外部创建委托(delegate)并将其传入而不是签名也是可以接受的,例如:
    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/

    相关文章:

    visual-studio - 在 Visual Studio 任务列表中隐藏 TODO 标签

    javascript - 调用节点模块失败,错误为 : Error: Uncaught (in promise): TypeError: Cannot read property 'toLowerCase' of undefined

    visual-c++ - 从 C++/CLI 项目创建纯 MSIL 程序集?

    c++ - 对象传播

    c++ - 是否可以从多个模板特化生成自动 `switch()` 指令?

    C# 找不到类型或命名空间 'Core'。 CS0246

    c# - 如何正确部署托管/非托管 C++ 库

    c++ - 在 Windows 窗体中将 char 转换为 system.string

    c++ - 不确定如何在 C++ 中处理这种模式

    c++ - 跳转二分查找有直观的解释吗?