簡體   English   中英

將轉發lambda轉換為函數指針

[英]Converting a forwarding lambda to a function pointer

這有兩件事可行。 我們可以實例化一個轉發函數模板來獲取一個帶左值的函數指針:

template <class T>
void f(T &&) {}

void(*p)(int &) = f; // Cool!

我們還可以將帶有左值的非捕獲通用lambda轉換為帶左值的函數指針:

auto l = [](auto &) { };

void (*lp)(int &) = l; // Still cool!

但顯然GCC和Clang都沒有將轉發通用lambda轉換為帶左值的函數指針:

auto l = [](auto &&) { };

void (*lp)(int &) = l; // Not cool!

GCC產出:

<source>:9:21: error: invalid user-defined conversion from '<lambda(auto:1&&)>' to 'void (*)(int&)' [-fpermissive]
 void (*lp)(int &) = l;
                     ^

Clang輸出:

<source>:9:8: fatal error: no viable conversion from '(lambda at <source>:7:10)' to 'void (*)(int &)'
void (*lp)(int &) = l;
       ^            ~
<source>:7:10: note: candidate template ignored: could not match 'type-parameter-0-0 &&' against 'int &'
auto l = [](auto &&) { };
         ^

盡管可以從轉發lambda獲取一個帶左值的成員函數指針,但這一切都是這樣的:

auto lmp = &decltype(l)::operator()<int &>;

template <class...>
struct check;
check<decltype(lmp)> c;

...按預期輸出類型void (<lambda(auto:1&&)>::*)(int&) const

我認為參考折疊規則是任何模板實例化所固有的,並且期望它能夠工作。 Clang和GCC都有錯誤,或標准實際上沒有提供?

TL; DR:這是根據標准指定的行為。 模板參數推導有一個特殊的規則,用於在獲取函數模板的地址時推導模板參數,允許轉發引用按預期工作。 轉換函數模板沒有這樣的規則。

注意:這看起來只是一個尚未編寫提案的領域。 如果有人為此撰寫提案,似乎可以在將來開展這項工作。


來自[expr.prim.lambda]

...... 對於沒有lambda-capture的通用lambda,閉包類型有一個指向函數的轉換函數模板。 轉換函數模板具有相同的發明模板參數列表,並且指向函數的指針具有與函數調用操作符模板相同的參數類型。 指向函數的指針的返回類型應該表現為它是一個decltype-specifier,表示相應函數調用操作符模板特化的返回類型。

重點補充

這表明必須以一對一的方式復制模板參數和函數參數類型:

// simplified version of the example in [expr.prim.lambda]/8
struct Closure {
    template <typename T>
    void operator()(T&& t) const {
        /* ... */
    }

    template <typename T>
    static void lambda_call_operator_invoker(T&& t) {
        Closure()(std::forward<T>(t));
    }

    // Exactly copying the template parameter list and function parameter types.
    template <typename T>
    using fn_type = void(*)(T&&);
    // using fn_type = void(*)(T); // this compiles, as noted later

    template <typename T>
    operator fn_type<T>() const {
        return &lambda_call_operator_invoker<T>;
    }
};

這無法在Clang,GCC和MSVC的所有三個上進行編譯這當然是令人驚訝的,因為我們期望在T&&參數上發生參考崩潰。

但是, 該標准不支持這一點。


標准的重要部分是[temp.deduct.funcaddr] (使用函數模板的地址推導模板參數)和[temp.deduct.conv] (推導轉換函數模板參數)。 關鍵的是, [temp.deduct.type]特別提及[temp.deduct.funcaddr] ,但不提及[temp.deduct.conv]

標准中使用的一些術語:

  • P是轉換模板的返回類型,或函數模板的類型
  • A是我們“試圖轉換為”的類型

類似地,如果P具有包含(T)的形式,則將P的相應參數類型列表([dcl.fct])的每個參數類型P i與相應參數的對應參數類型A i進行比較 - A的類型列表。 如果P和A是在獲取函數模板的地址([temp.deduct.funcaddr])時從演繹中產生的函數類型,或者是從函數聲明中推導出模板參數([temp.deduct.decl] ])和P i和A i分別是P和A的頂級參數類型列表的參數,如果它是轉發參考([temp.deduct.call])則調整P i並且A i是左值參考,在這種情況下,P i的類型被改變為模板參數類型(即, T&&變為簡單的T )。

重點補充

這特別要求獲取函數模板的地址,使轉發引用正常工作。 轉換函數模板沒有類似的引用。

如果我們將fn_type更改為void(*)(T) ,則重新訪問前面的示例,這與標准中描述的操作相同。

暫無
暫無

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

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