[英]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。
行錯誤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
,它也是合法的。
我想讓Line 錯誤1合法。 與在行OK 2中一樣 ,可以在沒有任何包裝器的情況下傳遞bar<int, double>
(與行OK 1不同)並保持相同的形式。 但是,參數類型不同。 我想傳遞為std::function
,而不是函數指針。
問題2。
行錯誤2 :: 1和2 :: 2 :我想在這里實現的是,我希望類Foo
推導出返回類型的bar
作為其類模板類型F
(對於上面的代碼, F
是double
)。 所以我可以傳遞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
巧妙地用type-erasure編寫,所以有一個構造函數可以接受任何可轉換為它需要的類型的可調用函數。 請注意,這與上面錯誤中的類型推導不同,因為這里我們已經為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_ptr
要為T
推導出什么,您可以允許編譯器為您實例化其余的指針:
test.func_ptr<int>(bar);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.