2014-06-13 130 views
3

我遇到了C++中的Member Detector成語,它是一種類型特徵,用於判斷類是否包含某個特定名稱的成員。但是,如果類型不是類,鏈接示例不能正常工作:我希望false結果適用於任何非類類型。一個可能的解決方案肯定是這樣的,使用boost::is_class<T>SFINAE用於類型實例

template<typename T> 
struct general_DetectX : boost::mpl::and_< 
    boost::is_class<T>, 
    DetectX<T> >::type 
{ }; 

bool hasX = general_DetectX<int>::value; // hasX = false 

但這個問題是關於爲什麼當初DetectX<T>產生錯誤的,而不是做的事情SFINAE。下面是鏈接碼的相關部分的摘錄(局部結構FallbackCheck<U,U>和類型定義ArrayOfOneArrayOfTwotype爲了簡潔移除):

template<typename T> 
class DetectX 
{ 
     struct Derived : T, Fallback { }; 

     template<typename U> 
     static ArrayOfOne & func(Check<int Fallback::*, &U::X> *); 

     template<typename U> 
     static ArrayOfTwo & func(...); 

    public: 
     enum { value = sizeof(func<Derived>(0)) == 2 }; 
}; 

它可以看出,DetectX::Derived使用任何過載分辨率以外,所以不會調用SFINAE處理錯誤的規則。但是,這是可以改變的地方使用Derived發生的重載決議的一部分:

template<typename T> 
class DetectX 
{ 
    template<typename U> 
    struct Derived : U, Fallback { }; 

    template<typename U> 
    static ArrayOfOne & func(Check<int Fallback::*, &Derived<U>::X> *); 

    template<typename U> 
    static ArrayOfTwo & func(...); 

public: 
    enum { value = sizeof(func<T>(0)) == 2 }; 
}; 

試圖實例第一func()過載時Derived<T>模板只實例化,所以爲什麼我仍然得到錯誤即使是修改後的版本?這裏有一個例子:

$ g++ --version | head -n1 
g++ (GCC) 4.8.2 
$ g++ -c demo.cxx 
demo.cxx: In instantiation of 'struct DetectX<int>::Derived<int>': 
demo.cxx:16:53: required by substitution of 'template<class U> static char (& DetectX<T>::func(DetectX<T>::Check<int DetectX<T>::Fallback::*, (& DetectX<T>::Derived<U>::X)>*))[1] [with U = U; T = int] [with U = int]' 
demo.cxx:24:31: required from 'class DetectX<int>' 
demo.cxx:27:25: required from here 
demo.cxx:7:12: error: base type 'int' fails to be a struct or class type 
    struct Derived : U, Fallback { }; 
      ^
+0

它失敗了,因爲替換髮生在基類列表*其中*任何錯誤都會導致硬錯誤而不是軟錯誤(SFINAE)。換句話說,SFINAE不會出現在基類中。 – Nawaz

+0

@Nawaz感謝您的評論,介紹我的硬和軟錯誤。但是我需要Jarod42在他的答案中提供的http://stackoverflow.com/questions/15260685鏈接來獲取上下文。 –

回答

2

您可以使用:(https://ideone.com/LArNVO

#include <cstdint> 
#include <type_traits> 

#define DEFINE_HAS_MEMBER(traitsName, memberName)        \ 
    template <typename U>              \ 
    class traitsName               \ 
    {                   \ 
    private:                 \ 
     struct Fallback { int memberName; };         \ 
     struct Dummy {};              \ 
     template<typename T, bool is_a_class = std::is_class<T>::value>  \ 
     struct identity_for_class_or_dummy { using type = Dummy; };   \ 
     template<typename T>             \ 
     struct identity_for_class_or_dummy<T, true> { using type = T; };  \ 
                       \ 
     template <typename Base>            \ 
     struct Derived : Base, Fallback {};         \ 
     template<typename T, T> struct helper;        \ 
     template<typename T>             \ 
     static std::uint8_t             \ 
     check(helper<int (Fallback::*),          \ 
       &Derived<typename identity_for_class_or_dummy<T>::type>::memberName>*); \ 
     template<typename T> static std::uint16_t check(...);     \ 
    public:                 \ 
     static                \ 
     constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint16_t); \ 
    } 

DEFINE_HAS_MEMBER(has_foo, foo); 

// Now test it: 
class C{ public: int foo; }; 
class D : public C {}; 
class E {}; 

static_assert(has_foo<C>::value, ""); 
static_assert(has_foo<D>::value, ""); 
static_assert(!has_foo<E>::value, ""); 
static_assert(!has_foo<int>::value, ""); 

你可以理解爲什麼它在你的情況下未能在以下問題What is exactly the 「immediate context」 mentioned in the C++11 Standard for which SFINAE applies?

總之SFINAE適用於Derived<U>::X但而不是Derived<U>(這是你的情況下形成的)。

+0

感謝您使用DEFINE_HAS_MEMBER示例,但我無法在此應用程序中使用它:1.它使用C++ 11,但我使用了C++ 03,我忘了提及它(小調:可以重寫)。 2.它需要成員簽名,而另一個解決方案則不需要。但是到http://stackoverflow.com/questions/15260685的鏈接真的很有幫助,基本上回答了我的問題:實例化Derived 發生在實例化func 的第一步,其中SFINAE不適用。 –

+0

對於* 2. *,我已經修復了我的實現。 (因爲'T :: foo'的簽名沒有被選中)。 – Jarod42

+0

謝謝我也用過你的技術。 –