2008-09-17 87 views
92

我在尋求模板技巧來檢測某個類是否具有給定簽名的特定成員函數。檢查一個類是否有給定簽名的成員函數

的問題是一個類似引用在這裏 http://www.gotw.ca/gotw/071.htm ,但不一樣的:在他回答的問題是C類必須提供一個成員函數與特定的簽名,否則程序韓元薩特的書的項目不會編譯。在我的問題中,如果一個類有這個功能,我需要做些事情,否則做「別的事」。

boost :: serialization面臨類似的問題,但我不喜歡他們採用的解決方案:默認情況下調用一個免費函數(您必須定義)的特定簽名的模板函數,除非您定義特定的成員函數(在他們的情況下,「序列化」使用給定類型的2個參數)和特定的簽名,否則會發生編譯錯誤。這是爲了實現侵入式和非侵入式系列化。

我不喜歡這樣的解決方案有兩個原因:

  1. 是非侵入你必須覆蓋全球的「序列化」功能,在升壓::系列化命名空間,所以你必須在客戶端代碼打開命名空間提升和命名空間序列化!
  2. 堆棧解決 混亂是10到12個函數調用。

我需要定義一個自定義行爲的類時,還沒有該成員函數,和我的實體是不同的命名空間內(我不希望重寫一個命名空間中定義一個全局函數,而我在另一個)

你能給我一個提示來解決這個難題嗎?

+1

Similar questions:http://stackoverflow.com/questions/257288 – 2009-08-25 20:02:47

+0

@ R.MartinhoFernandes你在找什麼樣的答案? [這個答案](http://stackoverflow.com/a/10707822/947836)由邁克金漢相當深入,並使用C++ 11的東西。 – jrok 2013-05-29 17:04:32

+0

@ R.MartinhoFernandes也許[這](http://stackoverflow.com/questions/14882588/correct-signature-of-detect-presence-of-containerreserve)是你正在尋找的現代版本? – 2013-05-29 19:36:52

回答

75

我不確定我是否正確理解您,但您可能會利用SFINAE在編譯時檢測函數的存在。從我的代碼示例(測試類是否具有成員函數size_t used_memory()const)。

template<typename T> 
struct HasUsedMemoryMethod 
{ 
    template<typename U, size_t (U::*)() const> struct SFINAE {}; 
    template<typename U> static char Test(SFINAE<U, &U::used_memory>*); 
    template<typename U> static int Test(...); 
    static const bool Has = sizeof(Test<T>(0)) == sizeof(char); 
}; 

template<typename TMap> 
void ReportMemUsage(const TMap& m, std::true_type) 
{ 
     // We may call used_memory() on m here. 
} 
template<typename TMap> 
void ReportMemUsage(const TMap&, std::false_type) 
{ 
} 
template<typename TMap> 
void ReportMemUsage(const TMap& m) 
{ 
    ReportMemUsage(m, 
     std::integral_constant<bool, HasUsedMemoryMethod<TMap>::Has>()); 
} 
+6

wtf是這個嗎?是合法的C++代碼?你能寫「template 」?? 但是...這是一個偉大的和新的解決方案!我感謝你,我會和同事一起分析更好的明天......太好了! – ugasoft 2008-09-17 21:39:18

+2

該示例缺少'int_to_type'的定義。很明顯,它不會增加答案,但這確實意味着人們可以在快速剪切和粘貼後看到您的代碼在運行。 – 2008-09-18 17:50:11

2

由於Koenig lookup要非侵入性,您還可以將serialize放入序列化類或歸檔類的命名空間中。有關更多詳細信息,請參閱Namespaces for Free Function Overrides。 :-)

打開任何給定的命名空間來實現一個免費的功能是簡單的錯誤。 (例如,你不應該打開名稱空間std來爲你自己的類型實現swap,但應該使用Koenig查找。)

2

好的。第二次嘗試。沒關係,如果你不喜歡這個,我正在尋找更多的想法。

香草薩特的文章談論特質。所以你可以有一個traits類,它的默認實例具有後備行爲,並且對於你的成員函數存在的每個類,那麼traits類專門用於調用成員函數。我相信Herb的文章提到了一種技術,以便它不涉及大量的複製和粘貼。

就像我說的,雖然,也許你不希望額外的工作涉及到「標記」實現該成員的類。在這種情況下,我正在尋找第三種解決方案......

+0

呃......我分析過這個解決方案......我覺得對我的框架用戶來說有點太貴了。 (好吧,我承認,我正在開發一個流式框架,並且我正在選擇擴展iostream還是重寫某些東西) – ugasoft 2008-09-17 21:34:42

+0

但我感謝您的建議:) – ugasoft 2008-09-17 21:35:40

+0

我的第三種解決方案是使用SFINAE。由於yrp的回答已經提到過,我不會進入它(因爲我仍在研究它:我知道這個想法,但魔鬼在細節中),除非他的解決方案最終不適用於你。 :-) – 2008-09-17 22:00:27

9

這應該是足夠的,如果你知道你期望的成員函數的名字。 (在這種情況下,函數bla在沒有成員函數的情況下無法實例化(編寫一個無論如何工作都很困難的函數,因爲缺少函數的部分特殊化,您可能需要使用類模板)。類似於enable_if)也可以在你希望它有一個成員函數類型模板。

template <typename T, int (T::*)()> struct enable { typedef T type; }; 
template <typename T> typename enable<T, &T::i>::type bla (T&); 
struct A { void i(); }; 
struct B { int i(); }; 
int main() 
{ 
    A a; 
    B b; 
    bla(b); 
    bla(a); 
} 
1

我相信你正在尋找的答案就在這裏。

http://www.martinecker.com/wiki/index.php?title=Detecting_the_Existence_of_Operators_at_Compile-Time

並在這裏稍微填寫一些例子

http://pastie.org/298994

我使用技術來檢測所討論的類的支撐ostream的操作者< <的存在,然後根據生成的代碼不同的比特。

我不相信有可能找到鏈接的解決方案,但它是一個非常整潔的把戲。花時間瞭解代碼,這是非常值得的。

布拉德

31

接受的答案給編譯時成員函數 內省的這個問題,雖然它是公正流行,具有可以在下面的程序中觀察到 一個障礙:內置

#include <type_traits> 
#include <iostream> 
#include <memory> 

/* Here we apply the accepted answer's technique to probe for the 
    the existence of `E T::operator*() const` 
*/ 
template<typename T, typename E> 
struct has_const_reference_op 
{ 
    template<typename U, E (U::*)() const> struct SFINAE {}; 
    template<typename U> static char Test(SFINAE<U, &U::operator*>*); 
    template<typename U> static int Test(...); 
    static const bool value = sizeof(Test<T>(0)) == sizeof(char); 
}; 

using namespace std; 

/* Here we test the `std::` smart pointer templates, including the 
    deprecated `auto_ptr<T>`, to determine in each case whether 
    T = (the template instantiated for `int`) provides 
    `int & T::operator*() const` - which all of them in fact do. 
*/ 
int main(void) 
{ 
    cout << has_const_reference_op<auto_ptr<int>,int &>::value; 
    cout << has_const_reference_op<unique_ptr<int>,int &>::value; 
    cout << has_const_reference_op<shared_ptr<int>,int &>::value << endl; 
    return 0; 
} 

與GCC 4.6.3,程序輸出110 - 通知我們 T = std::shared_ptr<int>確實不是提供int & T::operator*() const

如果你不是已經明白這個問題,那麼看看<memory>標題中的 std::shared_ptr<T>的定義將會清晰起來。在 的實現中,std::shared_ptr<T>從它繼承的基類 派生operator*() const。因此模板實例 SFINAE<U, &U::operator*>構成「發現」的運營商 U = std::shared_ptr<T>不會發生,因爲std::shared_ptr<T>在其自己的權利沒有 operator*()和模板實例並不 「做繼承」。

這阻礙不影響公知SFINAE的方法,使用「的的sizeof()詭計」, 用於僅檢測是否T有一些成員函數mf(參見例如 this answer和評論)。但是 確定T::mf存在通常(通常?)不夠好:您可能還需要確定它具有所需的簽名。這就是說明技術得分的地方。所需標記 的指示變體被寫入模板類型的參數中,該參數必須由用於SFINAE探針才能成功的 &T::mf來滿足。但是當T::mf被繼承時,這個實例化 技術的模板給出了錯誤的答案。

一個安全SFINAE技術爲T::mf編譯時內省必須避免 使用&T::mf模板參數中實例化一個類型賴以SFINAE 函數模板分辨率依賴。相反,SFINAE模板函數 分辨率只能依賴於使用的相關類型聲明 作爲重載SFINAE探測函數的參數類型。

通過回答由這個約束,我會 說明了編譯時檢測E T::operator*() const遵守問題的方式, 任意TE。相同的模式將應用作必要的修正 來探測任何其他成員方法簽名。

#include <type_traits> 

/*! The template `has_const_reference_op<T,E>` exports a 
    boolean constant `value that is true iff `T` provides 
    `E T::operator*() const` 
*/ 
template< typename T, typename E> 
struct has_const_reference_op 
{ 
    /* SFINAE operator-has-correct-sig :) */ 
    template<typename A> 
    static std::true_type test(E (A::*)() const) { 
     return std::true_type(); 
    } 

    /* SFINAE operator-exists :) */ 
    template <typename A> 
    static decltype(test(&A::operator*)) 
    test(decltype(&A::operator*),void *) { 
     /* Operator exists. What about sig? */ 
     typedef decltype(test(&A::operator*)) return_type; 
     return return_type(); 
    } 

    /* SFINAE game over :(*/ 
    template<typename A> 
    static std::false_type test(...) { 
     return std::false_type(); 
    } 

    /* This will be either `std::true_type` or `std::false_type` */ 
    typedef decltype(test<T>(0,0)) type; 

    static const bool value = type::value; /* Which is it? */ 
}; 

在這種解決方案中,重載SFINAE探測函數test()是 「調用 遞歸」。 (當然,實際上並沒有在所有調用,它只是具有 返回類型由編譯器解決了假想的調用。)

我們需要探測至少一個,並在信息最兩點:

  • T::operator*()是否存在?如果沒有,我們就完成了。
  • 鑑於T::operator*()存在,它的簽名是 E T::operator*() const

我們通過評估單次呼叫的返回類型 到test(0,0)來得到答案。這是通過做:

typedef decltype(test<T>(0,0)) type; 

此調用可能被解析爲 的test()/* SFINAE operator-exists :) */過載,也可能解析到/* SFINAE game over :(*/超載。 它無法解析爲/* SFINAE operator-has-correct-sig :) */超載, ,因爲那個只需要一個參數,而且我們正在傳遞兩個參數。

我們爲什麼要兩個?只需強制將分辨率排除在外 /* SFINAE operator-has-correct-sig :) */。第二個論點沒有其他意義。

該調用到test(0,0)將解析到/* SFINAE operator-exists :) */只是 在情況下,第一參數0 satifies第一參數類型的過載, 是decltype(&A::operator*),與A = T的。 0將滿足該類型 以防萬一T::operator*存在。

讓我們假設編譯器說是的。然後它與 /* SFINAE operator-exists :) */並且它需要確定函數調用的返回類型 ,在這種情況下它是decltype(test(&A::operator*)) - 返回類型的另一個調用test()

這次我們只傳遞一個參數&A::operator*,我們現在知道 存在,或者我們不會在這裏。對test(&A::operator*)的呼叫可能是 解析爲/* SFINAE operator-has-correct-sig :) */或再次解析爲 可能解析爲/* SFINAE game over :(*/。該呼叫將匹配 /* SFINAE operator-has-correct-sig :) */以防萬一&A::operator*滿足 過載的單參數類型,即E (A::*)() const, 與A = T

如果T::operator*具有所需的簽名,編譯器會在這裏說「是」, 然後再次必須評估過載的返回類型。現在不再有 「遞歸」:它是std::true_type

如果編譯器不選擇/* SFINAE operator-exists :) */爲 呼叫test(0,0)或不選擇/* SFINAE operator-has-correct-sig :) */ 呼叫test(&A::operator*),那麼在這兩種情況下它會與 /* SFINAE game over :(*/和最終的返回類型是std::false_type

這裏是一個測試程序,顯示了在不同樣本的案例(GCC 4.6.3再次)中產生預期的 答案的模板。

// To test 
struct empty{}; 

// To test 
struct int_ref 
{ 
    int & operator*() const { 
     return *_pint; 
    } 
    int & foo() const { 
     return *_pint; 
    } 
    int * _pint; 
}; 

// To test 
struct sub_int_ref : int_ref{}; 

// To test 
template<typename E> 
struct ee_ref 
{ 
    E & operator*() { 
     return *_pe; 
    } 
    E & foo() const { 
     return *_pe; 
    } 
    E * _pe; 
}; 

// To test 
struct sub_ee_ref : ee_ref<char>{}; 

using namespace std; 

#include <iostream> 
#include <memory> 
#include <vector> 

int main(void) 
{ 
    cout << "Expect Yes" << endl; 
    cout << has_const_reference_op<auto_ptr<int>,int &>::value; 
    cout << has_const_reference_op<unique_ptr<int>,int &>::value; 
    cout << has_const_reference_op<shared_ptr<int>,int &>::value; 
    cout << has_const_reference_op<std::vector<int>::iterator,int &>::value; 
    cout << has_const_reference_op<std::vector<int>::const_iterator, 
      int const &>::value; 
    cout << has_const_reference_op<int_ref,int &>::value; 
    cout << has_const_reference_op<sub_int_ref,int &>::value << endl; 
    cout << "Expect No" << endl; 
    cout << has_const_reference_op<int *,int &>::value; 
    cout << has_const_reference_op<unique_ptr<int>,char &>::value; 
    cout << has_const_reference_op<unique_ptr<int>,int const &>::value; 
    cout << has_const_reference_op<unique_ptr<int>,int>::value; 
    cout << has_const_reference_op<unique_ptr<long>,int &>::value; 
    cout << has_const_reference_op<int,int>::value; 
    cout << has_const_reference_op<std::vector<int>,int &>::value; 
    cout << has_const_reference_op<ee_ref<int>,int &>::value; 
    cout << has_const_reference_op<sub_ee_ref,int &>::value; 
    cout << has_const_reference_op<empty,int &>::value << endl; 
    return 0; 
} 

這個想法有新的缺陷嗎?它可以變得更通用,而不會再一次陷入它避免的障礙之中嗎?

3

在這裏非常有趣的與同一類問題來了自己,找到了提出的解決方案......但有一個解決方案的要求,即:

  1. 檢測繼承的函數;
  2. 是與非C++ 11個準備編譯器(所以沒有decltype)

發現了另一個thread提出了這樣的事情,根據BOOST discussion兼容。 下面是boost::has_*類的模型後,提議的解決方案作爲兩個宏特性類的聲明的泛化。

#include <boost/type_traits/is_class.hpp> 
#include <boost/mpl/vector.hpp> 

/// Has constant function 
/** \param func_ret_type Function return type 
    \param func_name Function name 
    \param ... Variadic arguments are for the function parameters 
*/ 
#define DECLARE_TRAITS_HAS_FUNC_C(func_ret_type, func_name, ...) \ 
    __DECLARE_TRAITS_HAS_FUNC(1, func_ret_type, func_name, ##__VA_ARGS__) 

/// Has non-const function 
/** \param func_ret_type Function return type 
    \param func_name Function name 
    \param ... Variadic arguments are for the function parameters 
*/ 
#define DECLARE_TRAITS_HAS_FUNC(func_ret_type, func_name, ...) \ 
    __DECLARE_TRAITS_HAS_FUNC(0, func_ret_type, func_name, ##__VA_ARGS__) 

// Traits content 
#define __DECLARE_TRAITS_HAS_FUNC(func_const, func_ret_type, func_name, ...) \ 
    template                 \ 
    < typename Type,              \ 
     bool is_class = boost::is_class<Type>::value       \ 
    >                   \ 
    class has_func_ ## func_name;            \ 
    template<typename Type>             \ 
    class has_func_ ## func_name<Type,false>         \ 
    {public:                 \ 
     BOOST_STATIC_CONSTANT(bool, value = false);       \ 
     typedef boost::false_type type;          \ 
    };                  \ 
    template<typename Type>             \ 
    class has_func_ ## func_name<Type,true>         \ 
    { struct yes { char _foo; };           \ 
     struct no { yes _foo[2]; };           \ 
     struct Fallback              \ 
     { func_ret_type func_name(__VA_ARGS__)       \ 
       UTILITY_OPTIONAL(func_const,const) {}       \ 
     };                 \ 
     struct Derived : public Type, public Fallback {};      \ 
     template <typename T, T t> class Helper{};       \ 
     template <typename U>             \ 
     static no deduce(U*, Helper           \ 
      < func_ret_type (Fallback::*)(__VA_ARGS__)     \ 
        UTILITY_OPTIONAL(func_const,const),      \ 
       &U::func_name             \ 
      >* = 0               \ 
     );                 \ 
     static yes deduce(...);            \ 
    public:                 \ 
     BOOST_STATIC_CONSTANT(            \ 
      bool,                \ 
      value = sizeof(yes)            \ 
       == sizeof(deduce(static_cast<Derived*>(0)))    \ 
     );                 \ 
     typedef ::boost::integral_constant<bool,value> type;     \ 
     BOOST_STATIC_CONSTANT(bool, is_const = func_const);     \ 
     typedef func_ret_type return_type;         \ 
     typedef ::boost::mpl::vector<__VA_ARGS__> args_type;    \ 
    } 

// Utility functions 
#define UTILITY_OPTIONAL(condition, ...) UTILITY_INDIRECT_CALL(__UTILITY_OPTIONAL_ ## condition , ##__VA_ARGS__) 
#define UTILITY_INDIRECT_CALL(macro, ...) macro (__VA_ARGS__) 
#define __UTILITY_OPTIONAL_0(...) 
#define __UTILITY_OPTIONAL_1(...) __VA_ARGS__ 

這些宏擴展爲具有以下原型traits類:

template<class T> 
class has_func_[func_name] 
{ 
public: 
    /// Function definition result value 
    /** Tells if the tested function is defined for type T or not. 
    */ 
    static const bool value = true | false; 

    /// Function definition result type 
    /** Type representing the value attribute usable in 
     http://www.boost.org/doc/libs/1_53_0/libs/utility/enable_if.html 
    */ 
    typedef boost::integral_constant<bool,value> type; 

    /// Tested function constness indicator 
    /** Indicates if the tested function is const or not. 
     This value is not deduced, it is forced depending 
     on the user call to one of the traits generators. 
    */ 
    static const bool is_const = true | false; 

    /// Tested function return type 
    /** Indicates the return type of the tested function. 
     This value is not deduced, it is forced depending 
     on the user's arguments to the traits generators. 
    */ 
    typedef func_ret_type return_type; 

    /// Tested function arguments types 
    /** Indicates the arguments types of the tested function. 
     This value is not deduced, it is forced depending 
     on the user's arguments to the traits generators. 
    */ 
    typedef ::boost::mpl::vector<__VA_ARGS__> args_type; 
}; 

那麼究竟是什麼典型的使用人能夠做出來的呢?

// We enclose the traits class into 
// a namespace to avoid collisions 
namespace ns_0 { 
    // Next line will declare the traits class 
    // to detect the member function void foo(int,int) const 
    DECLARE_TRAITS_HAS_FUNC_C(void, foo, int, int); 
} 

// we can use BOOST to help in using the traits 
#include <boost/utility/enable_if.hpp> 

// Here is a function that is active for types 
// declaring the good member function 
template<typename T> inline 
typename boost::enable_if< ns_0::has_func_foo<T> >::type 
foo_bar(const T &_this_, int a=0, int b=1) 
{ _this_.foo(a,b); 
} 

// Here is a function that is active for types 
// NOT declaring the good member function 
template<typename T> inline 
typename boost::disable_if< ns_0::has_func_foo<T> >::type 
foo_bar(const T &_this_, int a=0, int b=1) 
{ default_foo(_this_,a,b); 
} 

// Let us declare test types 
struct empty 
{ 
}; 
struct direct_foo 
{ 
    void foo(int,int); 
}; 
struct direct_const_foo 
{ 
    void foo(int,int) const; 
}; 
struct inherited_const_foo : 
    public direct_const_foo 
{ 
}; 

// Now anywhere in your code you can seamlessly use 
// the foo_bar function on any object: 
void test() 
{ 
    int a; 
    foo_bar(a); // calls default_foo 

    empty b; 
    foo_bar(b); // calls default_foo 

    direct_foo c; 
    foo_bar(c); // calls default_foo (member function is not const) 

    direct_const_foo d; 
    foo_bar(d); // calls d.foo (member function is const) 

    inherited_const_foo e; 
    foo_bar(e); // calls e.foo (inherited member function) 
} 
84

以下是依賴於C++ 11功能的可能實現。它正確地檢測到該功能,即使它是繼承的(不同於接受的答案中的解決方案,正如Mike Kinghan在his answer中觀察到的那樣)。

功能這個片斷測試的是所謂serialize

#include <type_traits> 

// Primary template with a static assertion 
// for a meaningful error message 
// if it ever gets instantiated. 
// We could leave it undefined if we didn't care. 

template<typename, typename T> 
struct has_serialize { 
    static_assert(
     std::integral_constant<T, false>::value, 
     "Second template parameter needs to be of function type."); 
}; 

// specialization that does the checking 

template<typename C, typename Ret, typename... Args> 
struct has_serialize<C, Ret(Args...)> { 
private: 
    template<typename T> 
    static constexpr auto check(T*) 
    -> typename 
     std::is_same< 
      decltype(std::declval<T>().serialize(std::declval<Args>()...)), 
      Ret // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
     >::type; // attempt to call it and see if the return type is correct 

    template<typename> 
    static constexpr std::false_type check(...); 

    typedef decltype(check<C>(0)) type; 

public: 
    static constexpr bool value = type::value; 
}; 

用法:

struct X { 
    int serialize(const std::string&) { return 42; } 
}; 

struct Y : X {}; 

std::cout << has_serialize<Y, int(const std::string&)>::value; // will print 1 
7

下面是一些使用片段: *這一切的膽量是越往下

檢查給定班級中的成員x。可能是變種,FUNC,類,聯合或枚舉:

CREATE_MEMBER_CHECK(x); 
bool has_x = has_member_x<class_to_check_for_x>::value; 

檢查成員函數void x()

//Func signature MUST have T as template variable here... simpler this way :\ 
CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x); 
bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value; 

檢查成員變量x

CREATE_MEMBER_VAR_CHECK(x); 
bool has_var_x = has_member_var_x<class_to_check_for_x>::value; 

檢查會員類x

CREATE_MEMBER_CLASS_CHECK(x); 
bool has_class_x = has_member_class_x<class_to_check_for_x>::value; 

檢查會員的工會x

CREATE_MEMBER_UNION_CHECK(x); 
bool has_union_x = has_member_union_x<class_to_check_for_x>::value; 

檢查成員枚舉x

CREATE_MEMBER_ENUM_CHECK(x); 
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value; 

檢查任何成員函數x不管簽名:

CREATE_MEMBER_CHECK(x); 
CREATE_MEMBER_VAR_CHECK(x); 
CREATE_MEMBER_CLASS_CHECK(x); 
CREATE_MEMBER_UNION_CHECK(x); 
CREATE_MEMBER_ENUM_CHECK(x); 
CREATE_MEMBER_FUNC_CHECK(x); 
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value; 

OR

CREATE_MEMBER_CHECKS(x); //Just stamps out the same macro calls as above. 
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value; 

詳細信息和核心:

/* 
    - Multiple inheritance forces ambiguity of member names. 
    - SFINAE is used to make aliases to member names. 
    - Expression SFINAE is used in just one generic has_member that can accept 
     any alias we pass it. 
*/ 

//Variadic to force ambiguity of class members. C++11 and up. 
template <typename... Args> struct ambiguate : public Args... {}; 

//Non-variadic version of the line above. 
//template <typename A, typename B> struct ambiguate : public A, public B {}; 

template<typename A, typename = void> 
struct got_type : std::false_type {}; 

template<typename A> 
struct got_type<A> : std::true_type { 
    typedef A type; 
}; 

template<typename T, T> 
struct sig_check : std::true_type {}; 

template<typename Alias, typename AmbiguitySeed> 
struct has_member { 
    template<typename C> static char ((&f(decltype(&C::value))))[1]; 
    template<typename C> static char ((&f(...)))[2]; 

    //Make sure the member name is consistently spelled the same. 
    static_assert(
     (sizeof(f<AmbiguitySeed>(0)) == 1) 
     , "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified." 
    ); 

    static bool const value = sizeof(f<Alias>(0)) == 2; 
}; 

宏(El躲閃!):

CREATE_MEMBER_CHECK:

//Check for any member with given name, whether var, func, class, union, enum. 
#define CREATE_MEMBER_CHECK(member)           \ 
                      \ 
template<typename T, typename = std::true_type>        \ 
struct Alias_##member;              \ 
                      \ 
template<typename T>              \ 
struct Alias_##member <              \ 
    T, std::integral_constant<bool, got_type<decltype(&T::member)>::value> \ 
> { static const decltype(&T::member) value; };        \ 
                      \ 
struct AmbiguitySeed_##member { char member; };        \ 
                      \ 
template<typename T>              \ 
struct has_member_##member {            \ 
    static const bool value             \ 
     = has_member<              \ 
      Alias_##member<ambiguate<T, AmbiguitySeed_##member>>   \ 
      , Alias_##member<AmbiguitySeed_##member>      \ 
     >::value               \ 
    ;                  \ 
} 

CREATE_MEMBER_VAR_CHECK:

//Check for member variable with given name. 
#define CREATE_MEMBER_VAR_CHECK(var_name)         \ 
                      \ 
template<typename T, typename = std::true_type>        \ 
struct has_member_var_##var_name : std::false_type {};      \ 
                      \ 
template<typename T>              \ 
struct has_member_var_##var_name<           \ 
    T                  \ 
    , std::integral_constant<            \ 
     bool                \ 
     , !std::is_member_function_pointer<decltype(&T::var_name)>::value \ 
    >                  \ 
> : std::true_type {} 

CREATE_MEMBER_FUNC_SIG_CHECK:

//Check for member function with given name AND signature. 
#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix) \ 
                      \ 
template<typename T, typename = std::true_type>        \ 
struct has_member_func_##templ_postfix : std::false_type {};    \ 
                      \ 
template<typename T>              \ 
struct has_member_func_##templ_postfix<          \ 
    T, std::integral_constant<            \ 
     bool                \ 
     , sig_check<func_sig, &T::func_name>::value       \ 
    >                  \ 
> : std::true_type {} 

CREATE_MEMBER_CLASS_CHECK:

//Check for member class with given name. 
#define CREATE_MEMBER_CLASS_CHECK(class_name)    \ 
                  \ 
template<typename T, typename = std::true_type>    \ 
struct has_member_class_##class_name : std::false_type {}; \ 
                  \ 
template<typename T>          \ 
struct has_member_class_##class_name<      \ 
    T              \ 
    , std::integral_constant<        \ 
     bool            \ 
     , std::is_class<         \ 
      typename got_type<typename T::class_name>::type \ 
     >::value           \ 
    >              \ 
> : std::true_type {} 

CREATE_MEMBER_UNION_CHECK:

//Check for member union with given name. 
#define CREATE_MEMBER_UNION_CHECK(union_name)    \ 
                  \ 
template<typename T, typename = std::true_type>    \ 
struct has_member_union_##union_name : std::false_type {}; \ 
                  \ 
template<typename T>          \ 
struct has_member_union_##union_name<      \ 
    T              \ 
    , std::integral_constant<        \ 
     bool            \ 
     , std::is_union<         \ 
      typename got_type<typename T::union_name>::type \ 
     >::value           \ 
    >              \ 
> : std::true_type {} 

CREATE_MEMBER_ENUM_CHECK:

//Check for member enum with given name. 
#define CREATE_MEMBER_ENUM_CHECK(enum_name)     \ 
                  \ 
template<typename T, typename = std::true_type>    \ 
struct has_member_enum_##enum_name : std::false_type {}; \ 
                  \ 
template<typename T>          \ 
struct has_member_enum_##enum_name<       \ 
    T              \ 
    , std::integral_constant<        \ 
     bool            \ 
     , std::is_enum<          \ 
      typename got_type<typename T::enum_name>::type \ 
     >::value           \ 
    >              \ 
> : std::true_type {} 

CREATE_MEMBER_FUNC_CHECK:

//Check for function with given name, any signature. 
#define CREATE_MEMBER_FUNC_CHECK(func)   \ 
template<typename T>       \ 
struct has_member_func_##func {     \ 
    static const bool value      \ 
     = has_member_##func<T>::value   \ 
     && !has_member_var_##func<T>::value  \ 
     && !has_member_class_##func<T>::value \ 
     && !has_member_union_##func<T>::value \ 
     && !has_member_enum_##func<T>::value \ 
    ;           \ 
} 

CREATE_MEMBER_CHECKS:

//Create all the checks for one member. Does NOT include func sig checks. 
#define CREATE_MEMBER_CHECKS(member) \ 
CREATE_MEMBER_CHECK(member);   \ 
CREATE_MEMBER_VAR_CHECK(member);  \ 
CREATE_MEMBER_CLASS_CHECK(member);  \ 
CREATE_MEMBER_UNION_CHECK(member);  \ 
CREATE_MEMBER_ENUM_CHECK(member);  \ 
CREATE_MEMBER_FUNC_CHECK(member) 
3

這裏是麥克金漢的回答更簡單服食。這將檢測繼承的方法。它還將檢查確切的簽名(與jrok允許參數轉換的方法不同)。

template <class C> 
class HasGreetMethod 
{ 
    template <class T> 
    static std::true_type testSignature(void (T::*)(const char*) const); 

    template <class T> 
    static decltype(testSignature(&T::greet)) test(std::nullptr_t); 

    template <class T> 
    static std::false_type test(...); 

public: 
    using type = decltype(test<C>(nullptr)); 
    static const bool value = type::value; 
}; 

struct A { void greet(const char* name) const; }; 
struct Derived : A { }; 
static_assert(HasGreetMethod<Derived>::value, ""); 

的Runnable example

2

要做到這一點,我們將需要使用:

  1. Function template overloading與根據該方法是否可用
  2. 與該元保持不同的返回類型type_traits標題中的條件,我們將要從我們的過載返回true_type or false_type
  3. 聲明true_type超載期待的intfalse_type超載期待可變參數參數利用:"The lowest priority of the ellipsis conversion in overload resolution"
  4. 在定義模板規範的true_type功能,我們將使用declvaldecltype使我們能夠檢測獨立的返回類型不同或重載函數方法之間

你可以看到這個here的活生生的例子,但我會在下面解釋:

我要檢查電子一個名爲test,這需要從類型轉換int功能xistence,那麼我需要聲明這兩個函數:

template <typename T, typename S = decltype(declval<T>().test(declval<int>))> static true_type hasTest(int); 
template <typename T> static false_type hasTest(...); 
  • decltype(hasTest<a>(0))::valuetrue(注意:沒有必要建立特殊的功能來處理的void a::test()過載時,void a::test(int)被接受)
  • decltype(hasTest<b>(0))::valuetrue(因爲int是可轉換到doubleint b::test(double)被接受時,獨立返回類型)
  • decltype(hasTest<c>(0))::valuefalsec不具有名爲test方法接受一個類型從int兌換爲此,這是不被接受)

這種解決方案具有2個缺點:

  1. 需要一個每方法聲明的一對函數
  2. 創建命名空間污染,特別是如果我們想測試類似的名稱,例如我們將命名一個函數,想測試一個test()方法?

因此,重要的是這些函數應該在細節名稱空間中聲明,或者理想情況下,如果它們僅用於類,則應該由該類私有聲明。爲此我寫了一個宏來幫您摘要信息:

#define FOO(FUNCTION, DEFINE) template <typename T, typename S = decltype(declval<T>().FUNCTION)> static true_type __ ## DEFINE(int); \ 
           template <typename T> static false_type __ ## DEFINE(...); \ 
           template <typename T> using DEFINE = decltype(__ ## DEFINE<T>(0)); 

您可以使用此類似:

namespace details { 
    FOO(test(declval<int>()), test_int) 
    FOO(test(), test_void) 
} 

隨後致電details::test_int<a>::valuedetails::test_void<a>::value會產生truefalse而言的內聯代碼或元編程。

0

沒有C++ 11的支持(decltype)這可能工作:

SSCCE

#include <iostream> 
using namespace std; 

struct A { void foo(void); }; 
struct Aa: public A { }; 
struct B { }; 

struct retA { int foo(void); }; 
struct argA { void foo(double); }; 
struct constA { void foo(void) const; }; 
struct varA { int foo; }; 

template<typename T> 
struct FooFinder { 
    typedef char true_type[1]; 
    typedef char false_type[2]; 

    template<int> 
    struct TypeSink; 

    template<class U> 
    static true_type &match(U); 

    template<class U> 
    static true_type &test(TypeSink<sizeof(matchType<void (U::*)(void)>(&U::foo))> *); 

    template<class U> 
    static false_type &test(...); 

    enum { value = (sizeof(test<T>(0, 0)) == sizeof(true_type)) }; 
}; 

int main() { 
    cout << FooFinder<A>::value << endl; 
    cout << FooFinder<Aa>::value << endl; 
    cout << FooFinder<B>::value << endl; 

    cout << FooFinder<retA>::value << endl; 
    cout << FooFinder<argA>::value << endl; 
    cout << FooFinder<constA>::value << endl; 
    cout << FooFinder<varA>::value << endl; 
} 

它是如何工作的希望

AAaB是clases問題,Aa感這是繼承我們正在尋找的成員的特殊之一。

FooFindertrue_typefalse_type是對應的C++ 11類的替代品。同樣爲了理解模板元編程,他們揭示了SFINAE-sizeof-trick的基礎。

TypeSink是一個模板結構,稍後用於將sizeof運算符的整體結果彙集到模板實例化中以形成一個類型。

match函數是另一種SFINAE類型的模板,沒有通用的對應模塊。因此,只有在它的參數類型匹配它專用的類型時才能實例化。

test函數和枚舉聲明一起最終形成中央SFINAE模式。有一個通用的使用省略號的方法,它返回false_type以及具有更多特定參數的對等物優先。

爲了能夠使用的T一個模板參數來實例化test功能,match功能必須被實例化,因爲它的返回類型需要實例化TypeSink說法。需要注意的是&U::foo被包裝在一個函數參數中,而不是在模板參數特化中被引用,所以繼承的成員查找仍然發生。

相關問題