2010-12-05 87 views
6

以下代碼顯示了一個SFINAE實現,用於檢查類型(基本上是一個類)在編譯時是否包含成員函數member_funcSFINAE方法比較

#define CHECKER(func_name,class_name) sizeof(class_name<T>::template func_name<T>(0)) == 1 
#include <iostream> 
struct A 
{ 
    void member_func(); 
}; 
struct B {}; 
template<typename T>struct Check_If_T_Is_Class_Type 
{ 
    template<typename C> static char func (char C::*p); 
    template<typename C> static long func (...); 
    enum{val = CHECKER(func,Check_If_T_Is_Class_Type)}; 
}; 

//APPROACH 1 
template <typename T>struct TypeHasMemberFunc 
{ 
    template <typename C, C> struct TypeCheck; 
    template <typename C> struct Prototype_Holder {typedef void (C::*fptr)();}; 
    template <typename C> static char func(TypeCheck 
              < 
               typename Prototype_Holder<C>::fptr, 
               &C::member_func 
              >*); 
    template <typename C> static long func(...); 
    enum {value = CHECKER(func,TypeHasMemberFunc)}; 
}; 

//APPROACH 2 
template <typename T>struct has_member_func 
{ 
    template<class C> static char func(char (*)[sizeof(&C::member_func)]); 
    template<class C> static long func(...); 
    enum{value = CHECKER(func,has_member_func)}; 
}; 
int main(){ 
if(Check_If_T_Is_Class_Type<A>::val) 
    std::cout<<TypeHasMemberFunc<A>::value; //using APPROACH 1 

if(Check_If_T_Is_Class_Type<B>::val) 
    std::cout<<has_member_func<B>::value; //using APPROACH 2 
} 

但是我的問題是您更喜歡哪種方法(方法1或方法2),爲什麼?
你發現在給定的方法有任何不一致嗎?如果是,請讓我知道。

P.S:假設sizeof(char)!= sizeof(long)

+0

另請參閱http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence/3627243#3627243。它有一些非常好的答案。 – FireAphis 2011-01-03 17:19:05

回答

-1

編輯:完成和糾正。

另一種方法,使用繼承的歧義,可能在功能上等同於您的方法2.我記得方法1存在問題(它使用G ++ 4.4.5編譯),因爲名稱解析觸發了錯誤而不是替換失敗。我不得不求助於:

template <class T> 
struct has_foo 
{ 
    struct fallback { void foo(...); }; 
    struct D : T, fallback { }; 

    template <typename U, U> struct K; 

    // Will be ambiguous for U = D iff T has a foo member function.                           
    // It doesn't trigger an error, since D will always have at least one                         
    // foo member function.                                     
    template <class U> static char (&test(K<void (fallback::*)(...), &U::foo>*))[1]; 
    template <class U> static char (&test(...))[2]; 

    static const bool value = sizeof(test<D>(0)) == 2; 
}; 

這適用於T是類的時候,所以你可能想要添加你的圖層來檢查T是否是類類型。

請注意,任何foo成員函數都將被檢測到。如果要檢查是否檢測到foo功能可在給定參數調用,你必須做SFINAE的另一層:

// Check whether foo can be called with an argument of type Arg 
// and yields an element of type Res. 
// If you need Res = void, this code does not work. 
template <class T, typename Arg, typename Res> 
struct check_foo 
{ 
    struct flag {}; 
    struct D : T { using T::foo; flag foo(...); }; 

    template <typename U> 
    static char (&test(U))[1]; 

    template <typename> static char (&test(...))[2]; 

    static Arg f(); 

    static const bool value = sizeof(test<Arg>(((D*)0)->foo(f()))) == 1; 
}; 
+0

`所以你可能想要添加另一個層來檢查T是否是一個類型`已經照顧。看看我的代碼示例。 `Check_If_T_Is_Class_Type`檢查一個類型`T`是否是類的類型。 – 2010-12-05 11:49:11

+0

@Prasoon:是的,我看到了。 – 2010-12-05 11:49:51

+0

@Alexandre C .....編譯錯誤`gcc-4.3.4`。 – Nawaz 2010-12-05 14:04:16

1

第二條本辦法不檢查函數類型(返回類型或參數類型),並且不適用於所有類型,不僅是班級類型。

0

我個人更喜歡第二種方法來玩,因爲它更短,更容易理解。但是GCC不會編譯它,所以你必須使用類似的東西爲GCC:

namespace detail 
{ 
    template<class C> char func(char (*)[sizeof(&C::member_func)]); 
    template<class C> long func(...); 
} 

template <typename T>struct has_member_func 
{ 
    enum{value = (sizeof(detail::func<T>(0)) == 1)}; 
}; 

而且,這將是很好擺脫CHECKER宏。它使你的代碼可讀性極低。

無論如何,我會從在生產代碼使用這樣的C++黑客(除了你是一個升壓團隊成員:-)

這樣的東西是容易出錯的避免,難以支持,編譯器但主要之間幾乎不便攜式要點是我不記得任何要求這種硬編碼C++的現實生活任務。