簡體   English   中英

Eric Niebler如何實現std :: is_function?

[英]How does Eric Niebler's implementation of std::is_function work?

上周,Eric Niebler在推特上發布了一個非常緊湊的std::is_function traits類實現:

#include <type_traits>

template<int I> struct priority_tag : priority_tag<I - 1> {};
template<> struct priority_tag<0> {};

// Function types here:
template<typename T>
char(&is_function_impl_(priority_tag<0>))[1];

// Array types here:
template<typename T, typename = decltype((*(T*)0)[0])>
char(&is_function_impl_(priority_tag<1>))[2];

// Anything that can be returned from a function here (including
// void and reference types):
template<typename T, typename = T(*)()>
char(&is_function_impl_(priority_tag<2>))[3];

// Classes and unions (including abstract types) here:
template<typename T, typename = int T::*>
char(&is_function_impl_(priority_tag<3>))[4];

template <typename T>
struct is_function
    : std::integral_constant<bool, sizeof(is_function_impl_<T>(priority_tag<3>{})) == 1>
{};

但它是如何工作的?

一般的想法

而不是列出所有有效的函數類型,例如cpprefereence.com上示例實現 ,此實現列出了所有函數的類型,然后只有在沒有匹配的情況下才解析為true

非功能類型列表包括(從下到上):

  • 類和聯合(包括抽象類型)
  • 可以從函數返回的任何內容(包括void和引用類型)
  • 數組類型

與任何非函數類型都不匹配的類型是函數類型。 請注意, std::is_function明確地將可調用類型(如lambdas)或具有函數調用運算符的類視為不是函數。

is_function_impl_

我們為每個可能的非函數類型提供了一個is_function_impl函數的重載。 函數聲明可能有點難以解析,所以讓我們將其分解為類和聯合案例:

template<typename T, typename = int T::*>
char(&is_function_impl_(priority_tag<3>))[4];

該行聲明了一個函數模板is_function_impl_ ,它接受一個類型為priority_tag<3>參數,並返回對4個char的數組的引用。 按照C的古代慣例,聲明語法因陣列類型的存在而變得非常復雜。

此函數模板采用兩個模板參數。 第一個是無約束的T ,但第二個是指向int類型的T成員的指針。 這里的int部分並不重要,即。 這甚至適用於沒有任何int類型成員的T 它的作用是它會導致T s的語法錯誤,而不是類或聯合類型。 對於其他類型,嘗試實例化函數模板將導致替換失敗。

類似的技巧用於priority_tag<2>priority_tag<1>重載,它們使用它們的第二個模板參數來形成僅為T s編譯的表達式,它們分別是有效函數返回類型或數組類型。 只有priority_tag<0>重載沒有這樣的約束第二模板參數,因此可以用任何T實例化。

總而言之,我們為is_function_impl_聲明了四個不同的重載,它們的輸入參數和返回類型不同。 它們中的每一個都使用不同的priority_tag類型作為參數,並返回對不同唯一大小的char數組的引用。

is_function標記調度

現在,在實例化is_function ,它用T實例化is_function_impl 請注意,由於我們為此函數提供了四種不同的重載,因此必須在此處進行重載解析。 由於所有這些重載都是功能模板 ,這意味着SFINAE有機會參與其中。

因此,對於函數(並且僅函數),除了具有priority_tag<0>的最常規的重載之外,所有重載都將失敗。 那么為什么實例化不總是解決那個重載,如果它是最普遍的那個? 由於我們重載函數的輸入參數。

請注意, priority_tag的構造方式是priority_tag<N+1>公開繼承priority_tag<N> 現在,因為is_function_impl在這里使用priority_tag<3>調用,所以該重載比其他重載更好地匹配重載解析,因此首先嘗試它。 只有當由於替換錯誤而失敗時,才會嘗試下一個最佳匹配,即priority_tag<2>重載。 我們以這種方式繼續,直到我們找到可以實例化的重載或者我們達到priority_tag<0> ,這不受約束並且將始終有效。 由於較高的prio重載涵蓋了所有非函數類型,因此只能對函數類型進行此類操作。

評估結果

我們現在檢查調用is_function_impl_返回的類型的大小以評估結果。 請記住,每個重載都會返回對不同大小的char數組的引用。 因此,我們可以使用sizeof來檢查選擇了哪個重載,並且只有在達到priority_tag<0>重載時才將結果設置為true

已知錯誤

Johannes Schaub在實施中發現了一個錯誤 不完整類類型的數組將被錯誤地分類為函數。 這是因為數組類型的當前檢測機制不適用於不完整類型。

暫無
暫無

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

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