【问题标题】:Extract type of input parameter in templated function taking a function as parameter在以函数为参数的模板函数中提取输入参数的类型
【发布时间】:2021-07-22 07:15:15
【问题描述】:

我有这个功能:

template<typename T, int (*F)(T&)>
void DoStuff(T& s)
{
    auto an = make_any<T>(s);
    cout << _GetDataFromAny<MyStruct, F>(an);
}

需要这样调用:

DoStuff&lt;MyStruct, Fun&gt;(s);

这很好用,但是我不喜欢指定第一个类型,因为它是第二个参数签名的一部分。我希望能够推断出这样我就可以调用:

DoStuff&lt;Fun&gt;(s);

但是,我不知道如何在模板中指定类型T需要从函数F的签名中推导出来。

这可能吗?

【问题讨论】:

    标签: c++ function templates signature


    【解决方案1】:

    你可以编写一个帮助器来推断返回int的函数指针的参数类型:

    template<typename T>
    T arg_type(int(*)(T&));
    

    然后稍微重写你的函数模板,将函数指针作为非类型模板参数,并从中找出参数类型

    template<auto F, typename T = decltype(arg_type(F))>
    void DoStuff(T& s) {
     // ...
    }
    

    【讨论】:

    • 我养成了只在写完后才重新加载的习惯,这样更容易看到另一个答案比我的更好,而不是试图竞争:P
    • 嗯,这很快!我还不能接受答案(所以不会让我)。
    • @largest_prime_is_463035818 是的,我知道你的意思。不过,这并不是真正的竞争。您的解决方案同样有效,可以发布它。
    • 当然,不是竞争,我是在开玩笑。我只是不知道我会添加什么。我可以尝试在没有auto 参数的情况下获取它,但它来晚了
    • 注意= decltype(arg_type(F)) 很少有用,因为T 是推导出来的。 (对auto func = &amp;DoStuff&lt;&amp;Fun&gt;;有用)
    【解决方案2】:

    免责声明:这个答案经过了一系列的编辑和更正,非常感谢 Jarod42 的耐心和帮助,以及 cigien 的富有成果的讨论。它比必要的时间长一点,但我觉得保留一点历史是值得的。它是:最简单的/我更喜欢的/对先前混乱的一些解释。如需快速解答,请阅读第二部分。


    简单

    您可以为函数指针使用auto 模板参数(C++17 起),并从参数中推导出T:

    template<auto F, typename T>
    void DoStuff(T& s)
    {
        int x = F(s);
    }
    
    int foo(double&){ return 42;}
    
    int main() {
        double x;
        DoStuff<&foo>(x);
    }
    

    Live Demo


    “权利”

    上面的缺点是F和T是“独立的”。您可以使用例如std::string y; 调用DoStuff&lt;&amp;foo&gt;(y),并且只有在调用F 时实例化才会失败。这可能会导致不必要的复杂错误消息,具体取决于您对F 和s 的实际操作。要在将错误的T 传递给DoStuff&lt;F&gt; 时已经在调用站点触发错误,您可以使用特征来推断F 的参数类型并直接将其用作DoStuff 的参数类型:

    template <typename T> struct param_type;
    
    template <typename R,typename P>
    struct param_type< R(*)(P&)> {
        using type = P;
    };
        
    template<auto F>
    void DoStuff(typename param_type<decltype(F)>::type& s)
    {
        int x = F(s);  // (1)
    }
    
    int foo(double&){ return 42;}    
    
    int main() {
        double x;
        DoStuff<foo>(x);
        std::string y; 
        DoStuff<foo>(y);  // (2) error 
    }
    

    现在,之前只会发生在模板 (1) 中的错误已经发生在 main (2) 中,并且错误消息更加清晰。

    Live Demo


    “错”

    主要出于好奇,考虑这种从函数指针推导参数类型的方式:

    template <typename T> struct param_type;
    
    template <typename R,typename P>
    struct param_type< R(*)(P&)> {
        using type = P;
    };
        
    template<auto F, typename T = typename param_type<decltype(F)>::type>
    void DoStuffX(T& s)
    {
    }
    
    int foo(double&){ return 42;}    
    
    int main() {
        double x;
        DoStuffX<foo>(x);
    }
    

    这是我最初的答案,但它实际上并没有像我想象的那样做。请注意,我实际上并没有在DoStuff 中调用F,令我惊讶的是,它编译了:

    int main() {
        std::string x;
        DoStuffX<foo>(x);
    }
    

    原因是当T 可以从传递的参数中推断出来时,没有使用默认模板参数(参见here)。也就是说,DoStuffX&lt;foo&gt;(x); 实际上实例化了DoStuffX&lt;foo,std::string&gt;。我们仍然可以通过以下方式获得默认值:

    int main() {
        std::string x;
        auto f_ptr = &DoStuffX<foo>;
        f_ptr(x);  // error
    }
    

    现在用std::string调用DoStuffX&lt;foo&gt;是编译器错误,因为这里DoStuffX&lt;foo&gt;被实例化为DoStuffX&lt;foo,double&gt;,使用了默认参数(没有参数可以用来推断T时DoStuffX 已实例化)。

    【讨论】:

    • template&lt;auto F&gt; void DoStuffY(typename param_type&lt;decltype(F)&gt;::type&amp; s) 是另一种选择。根本没有扣分。可以使用函数指针或常规调用(仅适用于预期类型)。
    • @Jarod42 这实际上应该是答案。一定是关于这个问题的东西,不知何故我忘记了我所知道的一切以及更多。我会编辑
    猜你喜欢
    • 1970-01-01
    • 2011-07-06
    • 1970-01-01
    • 2021-03-29
    • 2018-10-23
    • 2015-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多