【问题标题】:Passing (partially) templated template function as std::function(or function pointer)将(部分)模板化模板函数作为 std::function(或函数指针)传递
【发布时间】:2017-06-09 19:09:07
【问题描述】:
#include <vector>
#include <functional>

template<class F>
class Foo
{
public:
    template <class T>
    void std_function(std::function<F(std::vector<T>)> functor)
    {
        /* something */
    }

    template <class T>
    void func_ptr(F (*funtor)(std::vector<T>))
    {
        /* something else */
    }
};

template<class T, class F>
F bar(std::vector<T>)
{
    return F();
}

int main()
{
    Foo<double> test;
    std::function<double(std::vector<int>)> barz = bar<int, double>;

    test.std_function(bar<int, double>); //error 1
    test.std_function(barz); //OK 1
    test.func_ptr(bar<int, double>); //OK 2

    test.std_function(bar<int>); //error 2::1
    test.func_ptr(bar<int>); //error 2::2

    return 0;
}

问题 1。

Line error 1:我试图将显式实例化的模板函数(bar&lt;int, double&gt;)作为std::function 传递,但这是不合法的。

Line OK 1 :如果我将bar&lt;int, double&gt; 包装成std::function&lt;double(std::vector&lt;int&gt;)&gt; 并传递包装的仿函数,那么它现在是合法的。

Line OK 2 : 如果我通过Foo::func_ptr 传递bar&lt;int, double&gt;,它得到函数指针作为参数而不是std::function,它也是合法的。

我想让 Line error 1 合法。与 Line OK 2 一样,可以在没有任何包装的情况下传递 bar&lt;int, double&gt;(与 Line OK 1 不同)并保持相同的形式。但是,参数类型不同。我想作为std::function 传递,而不是函数指针。

问题 2。

Line error 2::1 and 2::2 :我想在这里实现的是,我希望 Foo 类将 bar 的返回类型推断为其类模板类型F(对于上面的代码,Fdouble)。所以我可以直接传递为bar&lt;int&gt;,而不是bar&lt;int, double&gt;

但它似乎推演失败,因为即使我通过Foo::func_ptr传递bar&lt;int&gt;,它仍然会产生错误。我怎样才能让这段代码按照我的意图工作?

【问题讨论】:

    标签: c++ c++11 templates c++14 function-templates


    【解决方案1】:

    对于错误 1,发生的事情是编译器试图替换 std::function 中的 T,但它不能,因为最终函数指针和 std::function 是不同的类型,并且没有为指向std::function的函数指针

    这行得通:

    std::function<double(std::vector<int>)> barz = bar<int, double>
    

    因为std::function 巧妙地使用类型擦除编写了一个构造函数,该构造函数可以接受任何可转换为所需类型的可调用对象。请注意,这与上述错误中的类型推断不同,因为这里我们已经为 std::function 指定了模板参数。

    请注意,我们可以做一些工作来让Foo::std_function 正常工作。首先更改其签名以获取转发引用:

    template <class T>
    void std_function(T&& functor){/*I'll talk about this in a bit*\}
    

    然后我们可以在内部构造我们的std::function(我不知道你想要传递给它的内容),方法是使用一些辅助结构来确定它的类型。对于函数指针,我们可以执行以下操作:

    // base
    template<class... T>
    struct function_type_impl;
    
    // specialization for function ptrs and static class fns
    template<class Ret, class... Args>
    struct function_type_impl<Ret(*)(Args...)>
    {
       using type = std::function<Ret(Args...)>;
    };
    
    // type alias so we don't need to keep typing typename ... ::type
    template<class... T>
    using function_type = typename function_type_impl<std::decay_t<T>...>::type;
    

    然后我们可以修改我们的std_function签名:

    template <class T>
    void std_function(T&& functor)
    {
        function_type<T> myFunction = std::forward<T>(functor);
        // do something with our std::function
    }
    

    那么你可以称它为

    test.std_function(&::bar<int, double>);
    

    但是,如果我们想要更完整,并接受函子、lambda 甚至其他 std::functions,我们可以添加更多特化:

    namespace detail
    {
    template<class... T>
    struct function_type_impl;
    
    template<class Callable>
    struct function_type_impl<Callable>
    {
        using type = typename function_type_impl<decltype(&Callable::operator())>::type;
    };
    
    template<class C, class Ret, class... Args>
    struct function_type_impl<Ret(C::*)(Args...) const>
    {
        using type = std::function<Ret(Args...)>;
    };
    
    template<class Ret, class... Args>
    struct function_type_impl<Ret(*)(Args...)>
    {
        using type = std::function<Ret(Args...)>;
    };
    
    template<class... T>
    using function_type = typename function_type_impl<std::decay_t<T>...>::type;
    }// detail namespace
    

    现在以下内容也将起作用:

    struct MyFunctor
    {
        double operator()(std::vector<int>) const
        {
            return 42;
        }
    };
    
    struct MyFunctor2
    {
        static double foo(std::vector<int>)
        {
            return 42;
        }
    };
    
    int main()
    {
        Foo<double> test;
        std::function<double(std::vector<int>)> barz = bar<int, double>;
        test.std_function(&::bar<int, double>);
        test.std_function(barz);
        test.std_function([](std::vector<int>)->double{return 42;});
        test.std_function(MyFunctor{});
        test.std_function(MyFunctor2::foo);
    }
    

    Live Demo

    对于错误 2::1 和 2::2,问题更简单;在完全实例化之前,函数根本不存在。也就是说,您不能创建指向部分模板化函数的函数指针。尝试获取函数指针时,您必须指定所有模板参数。由于您已经指定了返回类型,如果您明确告诉func_ptrT 推断什么,则可以允许编译器为您实例化指针的其余部分:

    test.func_ptr<int>(bar); 
    

    【讨论】:

    • 对于第 2 段:void counter_example( double(*)(std::vector<int>) ); -- 我可以将函数模板转换为函数指针,而无需传递任何模板参数。
    • @Yakk:嗯,我不知道。谢谢你的学习。帖子已更新。
    • @gear 不一定。 std::function 可以绑定到更广泛的东西,比如 lambdas。函数指针会更具体更快
    • 错误 1 ​​不是非推断上下文。由于类型不匹配,这是一个简单的推断失败。
    猜你喜欢
    • 2015-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-19
    • 1970-01-01
    相关资源
    最近更新 更多