将(部分)模板化的模板函数作为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传递,但这是不合法的。

OK 1:如果我将bar<int, double>封装到std::function<double(std::vector<int>)>中并传递封装的函子,那么它现在是合法的。

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

我想使行错误1合法。与LineOK 2一样,可以在没有任何包装的情况下传递bar<int, double>(与Line

OK 1问题2.

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

但它似乎没有通过推导,因为即使我通过bar<int>Foo::func_ptr,它仍然会产生错误。我如何才能使此代码按我的意图工作?

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

然而,如果我们想要更完整,并接受函子、lambdas,甚至其他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); 

相关内容

  • 没有找到相关文章

最新更新