通过(部分)模板化模板函数作为std :: function(或函数指针) [英] Passing (partially) templated template function as std::function(or function pointer)

查看:116
本文介绍了通过(部分)模板化模板函数作为std :: function(或函数指针)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

 #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.

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

确定1 :如果将bar<int, double>包装到std::function<double(std::vector<int>)>中并通过包装的函子,则现在是合法的.

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

我想使线路错误1 合法.与 OK 2 行中一样,可以通过bar<int, double>而不使用任何包装程序(不同于Line OK 1 )并保持相同的形式.但是,参数类型不同.我想作为std::function传递,而不是函数指针传递.

问题2.

条错误2 :: 1和2 :: 2 :我要在这里实现的是,我希望类Foo推断出bar的返回类型作为其类模板类型F(对于上面的代码,Fdouble).因此,我只能通过bar<int>,而不是bar<int, double>.

但是,这似乎无法通过推论得出结论,因为即使我通过Foo::func_ptr传递了bar<int>,它仍然会产生错误.如何使此代码按我的意图工作?

解决方案

对于错误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);
}

实时演示

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

test.func_ptr<int>(bar); 

#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;
}

Question 1.

Line error 1 : I am trying to pass explicitly instantiated template function(bar<int, double>) as std::function, but It is not legal.

Line OK 1 : If I wrap bar<int, double> into std::function<double(std::vector<int>)> and pass wrapped functor, it is legal now.

Line OK 2 : If I pass bar<int, double> through Foo::func_ptr, which gets function pointer as argument instead of std::function, it is also legal.

I want to make the Line error 1 legal. As in the Line OK 2, it is possible to pass bar<int, double> without any wrapper(unlike Line OK 1) and keep same form. But, the parameter type is different. I want to pass as std::function, not function pointer.

Question 2.

Line error 2::1 and 2::2 : What I am trying to achieve here is, I want class Foo to deduce return type of bar as its class template type F(for the code above, F is double). So I can just pass as bar<int>, not bar<int, double>.

But It seems to fail deduction, because even if I pass bar<int> through Foo::func_ptr, it still produces error. How can I make this code work as my intention?

解决方案

For error 1, what's happening is that the compiler is attempting to substitute for T in std::function, but it cannot because ultimately a function pointer and a std::function are different types, and there's no conversion defined for a function pointer to std::function

This worked:

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

Because std::function was cleverly written with type-erasure to have a constructor that can accept any callable that is convertible to the type it needs. Note that this isn't the same as type-deduction in the above error because here we are already specifying the template arguments for std::function.

Note that we can do a little work to get Foo::std_function to work properly. First change its signature to take a forwarding reference:

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

Then we can construct our std::function internally (What you're looking to pass into it, I don't know) by using some helper structs to determine its type. For a function pointer we can do the following:

// 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;

and then we can modify our std_function signature:

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

Then you can call it as

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

However, if we want to be more complete, and accept functors, lambdas, and even other std::functions, we can add more specializations:

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

And now the following will work, too:

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

For errors 2::1 and 2::2, the issue is simpler; functions do not exist at all until they're completely instantiated. That is, you cannot create a function pointer to a partially templated function. You have to specify all the template arguments when trying to get a function pointer. Since you have already specified the return type, you can allow the compiler to instantiate the rest of the pointer for you if you explicitly tell func_ptr what to deduce for T:

test.func_ptr<int>(bar); 

这篇关于通过(部分)模板化模板函数作为std :: function(或函数指针)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆