将(部分)模板化模板函数作为 std::function(或函数指针)传递
Passing (partially) templated template function as std::function(or function pointer)
#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:我试图将显式实例化的模板函数 (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 合法。与行 OK 2 一样,可以在没有任何包装的情况下传递 bar<int, double>
(不同于行 OK 1)并保持相同的形式.但是,参数类型不同。我想传递为 std::function
,而不是函数指针。
问题二
行错误2::1和2::2:我想在这里实现的是,我想要classFoo
推导出 bar
的 return 类型作为其 class 模板类型 F
(对于上面的代码,F
是 double
)。所以我可以只传递 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
是用 type-erasure 巧妙地编写的,以便拥有一个构造函数,该构造函数可以接受任何可转换为所需类型的可调用对象。请注意,这与上述错误中的 type-deduction 不同,因为我们已经在此处指定了 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,问题比较简单;函数在完全实例化之前根本不存在。也就是说,您不能创建指向部分模板化函数的函数指针。尝试获取函数指针时,您必须指定所有模板参数。由于您已经指定了 return 类型,如果您明确告诉 func_ptr
要为 T
推导什么,则可以允许编译器为您实例化指针的其余部分:
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;
}
问题一
行 错误 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 合法。与行 OK 2 一样,可以在没有任何包装的情况下传递 bar<int, double>
(不同于行 OK 1)并保持相同的形式.但是,参数类型不同。我想传递为 std::function
,而不是函数指针。
问题二
行错误2::1和2::2:我想在这里实现的是,我想要classFoo
推导出 bar
的 return 类型作为其 class 模板类型 F
(对于上面的代码,F
是 double
)。所以我可以只传递 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
是用 type-erasure 巧妙地编写的,以便拥有一个构造函数,该构造函数可以接受任何可转换为所需类型的可调用对象。请注意,这与上述错误中的 type-deduction 不同,因为我们已经在此处指定了 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,问题比较简单;函数在完全实例化之前根本不存在。也就是说,您不能创建指向部分模板化函数的函数指针。尝试获取函数指针时,您必须指定所有模板参数。由于您已经指定了 return 类型,如果您明确告诉 func_ptr
要为 T
推导什么,则可以允许编译器为您实例化指针的其余部分:
test.func_ptr<int>(bar);