2013-10-22 97 views
1

我有這樣的代碼,但它不會編譯:爲什麼我不能將繼承的錯誤用作SFINAE?

#include <iostream> 
#include <stdexcept> 
#include <cassert> 


template<class> struct id{}; 
template< class U, class V> struct base: public id<U>, public id<V> 
{ 
    static const bool value = true; 
}; 

template< class U, class V> 
struct is_different 
{ 

    typedef char (&true_)[1]; 
    typedef char (&false_)[2]; 

    template< class T, class K, bool = base<T,K>::value > 
    struct checker; 



    template< class T, class K> 
    static true_ test(checker<T,K>*); 

    template< class , class > 
    static false_ test(...); 


    static const bool value = sizeof(test<U,V>(0)) == sizeof(true_); 

}; 


int main (void) 
{ 
    bool b1 = is_different<int,float>::value; 
    bool b2 = is_different<int,int>::value; // <--- error 

    std::cout << std::boolalpha << b1 << '\n' 
           << b2 << '\n'; 
    return 0;  
} 

錯誤:

main.cpp: In instantiation of ‘struct base<int, int>’: 
main.cpp:25:17: required by substitution of ‘template<class T, class K> static char (& is_different<U, V>::test(is_different<U, V>::checker<T, K>*))[1] [with T = T; K = K; U = int; V = int] [with T = int; K = int]’ 
main.cpp:31:41: required from ‘const bool is_different<int, int>::value’ 
main.cpp:39:38: required from here 
main.cpp:7:36: error: duplicate base type ‘id<int>’ invalid 
template< class U, class V> struct base: public id<U>, public id<V> 

爲什麼我不能使用重複繼承的失敗作爲SFINAE?

+0

我會說,因爲模板參數替換並不必然涉及實例化模板類,這將需要檢測來自相同類型的繼承。 SFINAE將會是不一致的,它是其中的一部分。 – jrok

回答

1

SFINAE只適用於直接在函數簽名中的東西。編譯錯誤發生在base<T, K>的實例化中,這是從函數簽名中刪除的兩個步驟。

這就是說,你爲什麼這麼難嗎?

template <typename T, typename U> 
struct is_same { static const bool value = false; }; 

template <typename T> 
struct is_same<T, T> { static const bool value = true; }; 

template <typename T, typename U> 
struct is_different { static const bool value = !is_same<T, U>::value; }; 

編輯:

因此,要回答在評論你的問題,讓我解釋一點關於SFINAE。該標準在17.8.2p8中規定了SFINAE:

If a substitution results in an invalid type or rexpression, type deduction fails. An invalid type or rexpression is one that would be ill-formed if written using the substituted arguments. Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of the class template specializations and/or function template specializations, the generation of implicitly-defined functions, et. Such side effects are not in the "immediate context" and can result in the program being ill-formed. - end note]

這裏有問題的是「直接上下文」。你在這裏做了兩件事,從直接上下文中消除你的錯誤。

  • 在評估默認參數表達式checker期間發生錯誤。儘管此評估是由參數列表checker<T,K>在參數列表test中觸發的,該參數列表爲獲得重載解析而實例化,但它不是直接上下文。您可以通過將base的定義更改爲template <typename T, typename U> struct base {};來嘗試此操作。這會使base的實例化中的錯誤消失,將其替換爲默認參數表達式的上下文中的錯誤(「沒有名爲value的成員」)。但是SFINAE仍然不會觸發;你仍然會遇到一個硬性錯誤。
  • 該錯誤發生在base定義的實例化中。這是從重載解決方案中移除的又一步驟。出於同樣的原因,將成員放入base中的T::foobar類型對於確定T是否包含類型foobar不起作用:base的定義不再是SFINAE上下文。您可以通過從圖片中刪除checker來看到單單是一個錯誤,例如,這樣做:

    template struct base:public id,public id { typedef void type; }; 模板struct is_different struct true_ {char c [1]; }; //我不喜歡sizeof(引用類型), struct false_ {char c [2]; }; //讓我緊張。 template static true_test(typename base :: type *); template static false_test(...);

    static const bool value = sizeof(test(0))== sizeof(true_); };

現在base實例化並不能掩蓋一個模板默認參數表達的背後,卻是正確的,在簽名,但你還是會得到一個錯誤。

+0

你能舉出另一個例子嗎?即哪個SFINAE沒有工作。對於我來說,如何實現is_same,is_different或其他什麼,我很有趣,只有SFINAE規則,我不能使用繼承失敗。 –

+0

新增了一個詳盡的解釋。 –