2012-03-07 56 views
3

Checking a member exists, possibly in a base class, C++11 version中,我們開發了一個C++ 11版本的經典成員檢查類型特徵SFINAE to check for inherited member functions,它也適用於C++ 11 final類,但使用C++ 11層的功能(即,decltype),得:檢查一個成員是否存在,可能在一個基類中,VS2005/08版本

template<typename T> 
class has_resize_method { 
    struct Yes { char unused[1]; }; 
    struct No { char unused[2]; }; 
    static_assert(sizeof(Yes) != sizeof(No)); 

    template<class C> 
    static decltype(std::declval<C>().resize(10), Yes()) test(int); 
    template<class C> 
    static No test(...); 
public: 
    static const bool value = (sizeof(test<T>(0)) == sizeof(Yes)); 
}; 

MSVC已final作爲名爲因爲VS2005 sealed非標準擴展,但decltype只在VS2010被添加。這留下了VS2005和2008,其中一個標記爲sealed的類仍然打破了傳統的類型特徵,並且C++ 11版本不能使用。

那麼,有沒有一種方法可以制定has_resize_method,這樣它也可以在類VC2005/08 sealed上運行?很顯然,就像使用C++ 11-only特性來解決C++ 11-only問題(final)一樣好,所以將使用VS-only擴展來解決VS2005/08-only問題sealed類的問題,但如果有一種解決方案適用於所有三套編譯器{C++ 11,{VS2005,VS2008},所有其他},那將很酷,但可能要求太多:)

+0

您可以用最小的代碼示例來擴展您的問題,以便更大的受衆更清楚地瞭解問題。 – iammilind 2012-03-07 11:08:06

+0

'template static decltype(std :: declval ().resize(10),Yes())test(int);'不能在MSVC2010下編譯(意外的文件結尾)。由於代碼格式良好,它似乎是一個編譯器錯誤。 – 2012-03-08 09:58:49

+0

@dark_charlie:它可以與'static_cast (0) - > resize(10)'而不是'std :: declval'一起使用嗎? – 2012-03-08 10:57:11

回答

4

我能夠想出一個適用於所有主要編譯器的解決方案。不幸的是,有一個MSVC預處理器檢查,因爲它抱怨其他編譯器的解決方案。主要區別在於MSVC不接受sizeof()內的函數指針,相反,GCC在檢查中似乎不接受(&C::resize == 0)。鏗鏘愉快地接受兩者。

#include <iostream> 

class Base { 
public: 
    void resize(int, int, int) { } 
}; 

class Derived : public Base { 

}; 

class Unrelated { }; 

template<typename T> 
class has_resize_method { 
    struct Yes { char unused[1]; }; 
    struct No { char unused[2]; }; 

#ifdef _MSC_VER 
    template <class C> 
    static Yes test(char (*)[(&C::resize == 0) + 1]); 
#else 
    template <class C> 
    static Yes test(char (*)[sizeof(&C::resize) + 1]); 
#endif 
    template<class C> 
    static No test(...); 
public: 
    static const bool value = (sizeof(test<T>(0)) == sizeof(Yes)); 
}; 

int main() { 
    std::cout << (has_resize_method<Base>::value ? "Base has method resize" : "Base has NO method resize") << std::endl; 
    std::cout << (has_resize_method<Derived>::value ? "Derived has method resize" : "Derived has NO method resize") << std::endl; 
    std::cout << (has_resize_method<Unrelated>::value ? "Unrelated has method resize" : "Unrelated has NO method resize") << std::endl; 
    return 0; 
} 

輸出:

Base has method resize 
Derived has method resize 
Unrelated has NO method resize 

在測試了GCC 4.5.3,GCC 4.3.4,鐺3.0時,Visual C++ 2008和Visual C++ 2010年我沒有訪問到Visual C++ 2005,但我認爲它也可以在那裏工作。它也編譯在Comeau Online,但我不能保證它在那裏產生一個正確的輸出。

適用於final和__sealed類。

請注意,它不僅檢查成員函數,而且檢查成員指針。如果此行爲不需要,您可能需要添加其他檢查,例如boost::is_member_function_pointer。同樣,您可能想要添加對參數/參數類型/結果類型數量的檢查 - 同樣,boost在這裏將非常有用, boost type decomposition

2

自vs2005起,MSVC有特別聲明__if_existsMSDN Link here。您可以使用它直接檢查成員函數名稱。然後檢查簽名。以下是一個簡單的foo檢測示例:

template <typename T, typename U> 
int8_t FooCheck(void(T::*)(U)) 
{ 
    return 0; 
} 

template <typename T> 
int16_t FooCheck(void(T::*)(double)) 
{ 
    return 0; 
} 

template <typename T> 
int32_t FooCheck(void(T::*)(int)) 
{ 
    return 0; 
} 


template <typename T> 
class Detector 
{ 
public: 

    __if_exists(T::foo) 
    {  
     enum 
     { 
      value = sizeof(FooCheck(&T::foo)) 
     }; 
    } 
    __if_not_exists(T::foo) 
    { 
     enum 
     { 
      value = 0 
     }; 
    } 

}; 


std::cout << Detector<Class>::value << std::endl; 
+0

請重新配置爲一種類型特徵,以便'has_resize_method :: value'是一個編譯時常量,如同在問題中一樣。但'__if_exists'的確看起來很有希望。 – 2012-03-10 07:04:47

相關問題