C++ std::enable_if解析


C++ std::enable_if解析

引言

今日在閱讀LLVM相關源碼時(如下所示),遇到了enable_if<>這個概念,以前從沒有遇到過,這里做個小記。

  1.  
    /*----------llvm/include/llvm/ADT/Hashing.h------------*/
  2.  
    /// \brief Compute a hash_code for any integer value.
  3.  
    ///
  4.  
    /// Note that this function is intended to compute the same hash_code for
  5.  
    /// a particular value without regard to the pre-promotion type. This is in
  6.  
    /// contrast to hash_combine which may produce different hash_codes for
  7.  
    /// differing argument types even if they would implicit promote to a common
  8.  
    /// type without changing the value.
  9.  
     
  10.  
    template <typename T>
  11.  
    typename std::enable_if<is_integral_or_enum<T>::value, hash_code>::type
  12.  
    hash_value(T value);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

enable_if 的主要作用就是當某個 condition 成立時,enable_if可以提供某種類型。enable_if在標准庫中通過結構體模板實現的,聲明如下:

template<bool Cond, class T = void> struct enable_if;
  • 1
  • 1
  • 1

英文解釋如下:

Enable type if the condition is met. 
The type T is enabled as member type enable_if::type if Cond is true. Otherwise, enable_if::type is not defined.

但是當 condition 不滿足的時候,enable_if<>::type 就是未定義的,當用到模板相關的場景時,只會 instantiate fail,並不會編譯錯誤,這時很重要的一點。為了理解 std::enable_if<> 的實現,我們先來了解一下 SFINAE。


SFINAE

SFINAE 是C++ 的一種語言屬性,具體內容就是”從一組重載函數中刪除模板實例化無效的函數”。

Prune functions that do not yield valid template instantiations from a set of overload functions.

SFINAE 的的全稱是 Substitution Failure Is Not An Error。

SFINAE 應用最為廣泛的場景是C++中的 std::enable_if,這里有完整的英文描述

In the process of template argument deduction, a C++ compiler attempts to instantiate signatures of a number of candidate overloaded functions to make sure that exactly one overloaded function is available as a perfect match for a given function call.

從上面的描述中我們可以看到,在對一個函數調用進行模板推導時,編譯器會嘗試推導所有的候選函數(重載函數,模板,但是普通函數的優先級要高),以確保得到一個最完美的匹配。

If an invalid argument or return type is formed during the instantiation of a function template, the instantiation is removed from the overload resolution set instead of causing a compilation error. 
As long as there is one and only one function to which the call can be dispatched, the compiler issues no errors.

也就是說在推導的過程中,如果出現了無效的模板參數,則會將該候選函數從重載決議集合中刪除,只要最終得到了一個 perfect match ,編譯就不會報錯。

如下代碼所示:

  1.  
    long multiply(int i, int j) { return i * j; }
  2.  
     
  3.  
    template <class T>
  4.  
    typename T::multiplication_result multiply(T t1, T t2)
  5.  
    {
  6.  
    return t1 * t2;
  7.  
    }
  8.  
     
  9.  
    int main(void)
  10.  
    {
  11.  
    multiply( 4, 5);
  12.  
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

main 函數調用 multiply 會使編譯器會盡可能去匹配所有候選函數,雖然第一個 multiply 函數明顯是較優的匹配,但是為了得到一個最精確的匹配,編譯器依然會嘗試去匹配剩下的候選函數,此時就會去推導 第二個multiply函數,中間在參數推導的過程中出現了一個無效的類型 int::multiplication_result ,但是因為 SFINAE 原則並不會報錯。


std::enable_if<> 的實現

前面我們在介紹 std::enable_if 的時候提到,如果 condition 不滿足的話,會生成一個無效的類型,此處由於SFINAE 機制的存在,只要 call 存在一個匹配的話,就不會報錯(只是簡單的丟棄該函數)。

std::enable_if<>的實現機制如下代碼所示:

  1.  
    template<bool Cond, typename T = void> struct enable_if {};
  2.  
     
  3.  
    template<typename T> struct enable_if<true, T> { typedef T type; };
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

從上面的代碼可以看到

在 condition 為真的時候,由於偏特化機制,第二個結構體模板明顯是一個更好的匹配,所以 std::enable_if<>::type 就是有效的。

當 condition 為假的時候,只有第一個結構體模板能夠匹配,所以std::enable_if<>::type 是無效的,會被丟棄,編譯器會報錯:error: no type named ‘type’ in ‘struct std::enable_if<false, bool>。

 

Note: 下面是Visual Studio 2013的實現:

  1.  
    // TEMPLATE CLASS enable_if
  2.  
    template<bool _Test,
  3.  
    class _Ty = void>
  4.  
    struct enable_if
  5.  
    { // type is undefined for assumed !_Test
  6.  
    };
  7.  
     
  8.  
    template<class _Ty>
  9.  
    struct enable_if<true, _Ty>
  10.  
    { // type is _Ty for _Test
  11.  
    typedef _Ty type;
  12.  
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

std::enable_if<> 使用及使用

std::enable_if<> 的使用原型如下所示:

template <bool Cond, class T = void> struct enable_if;
  • 1
  • 1
  • 1
  • Cond, A compile-time constant of type bool
  • T, A type.

使用 enable_if 的時候,對參數有這兩方面的限制。

member type definition
type T (defined only if Cond is true)

該例子來自於 這里

  1.  
    // enable_if example: two ways of using enable_if
  2.  
    #include <iostream>
  3.  
    #include <type_traits>
  4.  
     
  5.  
    // 1. the return type (bool) is only valid if T is an integral type:
  6.  
    template <class T>
  7.  
    typename std::enable_if<std::is_integral<T>::value,bool>::type
  8.  
    is_odd (T i) {return bool(i%2);}
  9.  
     
  10.  
    // 2. the second template argument is only valid if T is an integral type:
  11.  
    template < class T,
  12.  
    class = typename std::enable_if<std::is_integral<T>::value>::type>
  13.  
    bool is_even (T i) {return !bool(i%2);}
  14.  
     
  15.  
    int main() {
  16.  
     
  17.  
    short int i = 1; // code does not compile if type of i is not integral
  18.  
     
  19.  
    std::cout << std::boolalpha;
  20.  
    std::cout << "i is odd: " << is_odd(i) << std::endl;
  21.  
    std::cout << "i is even: " << is_even(i) << std::endl;
  22.  
     
  23.  
    return 0;
  24.  
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM