[英]implicit convert from lambda to std::function inside variadic template
我想實現一個以lambda為參數的模板函數。
#include <functional>
template<typename ... Result> using Fun = std::function<void(Result ...)>;
template<typename ... Result> void yield(Fun<Result ...>&& body) {};
template <typename T>
struct identity {
typedef T type;
};
template<typename ... Result> void yield2(typename identity<Fun<Result ...>>::type && body) {};
int main() {
yield<char>(
Fun<char>(
[](char) -> void {} // 1. success
)
);
yield2<char>(
[](char) -> void {} // 2. success with identify
);
yield<char>(
[](char) -> void {} // 3. fail, seems achievable
);
yield(
[](char) -> void {} // 4. fail, impossible ?
);
return 0;
}
案例3為何失敗? 我已經將模板參數提供給模板,因此它應該能夠推導出函數類型並將lambda隱式轉換為函數
編輯:
編譯器總是從函數參數中減少模板參數,我們可以反轉嗎?
yield<char>(
[](auto&& c) -> void {} // 5. is it possible ?
);
問題是你有一個可變參數模板,它開始嘗試查看它除了你的顯式char
參數之外還能推斷出什么。
如果你有一個像這樣的參數模板:
template<typename Result> using Fun = std::function<void(Result)>;
template<typename Result> void yield(Fun<Result>&& body) {};
你會注意到的
yield<char>(
[](char) -> void {} // 3. fail, seems achievable
);
完全沒有問題,因為整個std::function
是可以推斷的。
但是只要我們制作一個可變參數模板,我們的編譯器就會變得不快樂:
template<class... Result> using Fun = std::function<void(Result...)>;
template<class... Result> void yield(Fun<Result...>&& body) {};
這是因為,無論喜歡與否,編譯器將嘗試為Fun<Result...>
推導出更多模板參數,給定傳入的值 ([temp.deduct.type])。
yield2
回避了這個問題,因為它將結果類型放入非推導的上下文中,但由於您明確指定了模板參數,因此它將僅使用那些顯式指定的參數( char
)來推斷類型(#1的工作原理基本相同)。
我認為最好的解決方法是你的yield2
嘗試,但是你也可以這樣做以防止從參與類型推導傳遞的值:
auto fn = &yield<char>;
fn(
[](char) -> void {}
);
另一個解決方法是static_cast
你的調用yield
正確的類型:(我真的只是閱讀[tempddeduct.type]的“非推斷的上下文是:”下的其他可能的解決方法)
using fn_type = void(*)(Fun<char>&&);
static_cast<fn_type>(&yield)(
[](char) -> void {}
);
編輯:最后,您可以編寫一些額外的樣板模板,以使調用看起來更好(接近您的#4)。 請記住,這是一個不完整的impl例如:
這個模板的目標是檢測lambda的operator()
函數並提取它的返回類型和參數,這樣我們可以在調用yield
時顯式指定Fun
類型(因為你只使用void
所以不需要提取返回類型) :
首先是一個幫助器結構,它允許我們檢測不可變的lambda的返回類型和參數類型:
template<class T>
struct Fun_Type;
template<class C, class Ret, class... Args>
struct Fun_Type<Ret(C::*)(Args...) const>
{
using type = Fun<Args...>;
};
第二,我們的輔助函數call_yield
,它將Fun_Type<...>::type
傳遞給一個調用yield
:
template<class ImmutableLambda>
void call_yield(ImmutableLambda&& c)
{
using Fun_t = typename Fun_Type<decltype(&ImmutableLambda::operator())>::type;
yield(Fun_t{std::forward<ImmutableLambda>(c)});
}
現在我們可以簡單地稱它為:
int main() {
call_yield(
[](char) -> void {}
);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.