簡體   English   中英

在variadic模板中從lambda隱式轉換為std :: function

[英]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 {}
    );
}

演示 (C ++ 11)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM