将(部分)模板化模板函数作为std :: function(或函数指针)传递

suh*_*hwi 8 c++ templates function-templates c++11 c++14

#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;
}
Run Code Online (Sandbox Code Playgroud)

问题1.

错误1:我试图将显式实例化的模板函数(bar<int, double>)作为传递std::function,但它不合法.

OK 1:如果我换bar<int, double>std::function<double(std::vector<int>)>,并通过包函子,现在是合法的.

OK 2:如果我bar<int, double>通过Foo::func_ptr,它将函数指针作为参数而不是std::function,它也是合法的.

我想让Line 错误1合法.如在行OK 2中,可以在bar<int, double>没有任何包装器的情况下通过(与行OK 1不同)并保持相同的形式.但是,参数类型不同.我想传递as std::function,而不是函数指针.

问题2.

错误2 :: 1和2 :: 2:我想在这里实现的是,我希望类Foo推导返回类型bar为其类模板类型F(对于上面的代码,Fdouble).所以我可以通过bar<int>,而不是bar<int, double>.

但它似乎失败了,因为即使我bar<int>通过Foo::func_ptr,它仍然会产生错误.我怎样才能使这段代码成为我的意图?

And*_*dyG 5

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

这有效:

std::function<double(std::vector<int>)> barz = bar<int, double>
Run Code Online (Sandbox Code Playgroud)

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

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

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

然后我们可以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;
Run Code Online (Sandbox Code Playgroud)

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

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

然后你可以称之为

test.std_function(&::bar<int, double>);
Run Code Online (Sandbox Code Playgroud)

但是,如果我们想要更完整,并接受函子、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
Run Code Online (Sandbox Code Playgroud)

现在以下也将起作用:

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);
}
Run Code Online (Sandbox Code Playgroud)

现场演示

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

test.func_ptr<int>(bar); 
Run Code Online (Sandbox Code Playgroud)

  • 对于第 2 段: [void counter_example( double(*)(std::vector&lt;int&gt;) );](http://coliru.stacked-crooked.com/a/eb9979b58f3f76e7) -- 我可以将函数模板转换为不传递任何模板参数的函数指针。 (2认同)