2014-06-13 6 views
3

Я столкнулся с idiom Member Detector в C++, что является признаком типа, чтобы узнать, содержит ли класс член определенного имени. Но связанный пример не работает, как я ожидал, если тип не является классом: мне нужен результат 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 вещь. Вот выдержка из соответствующих частей связанного кода (местные Структуры Fallback и Check<U,U> и определение типов ArrayOfOne, ArrayOfTwo и type удалены для краткости):

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 }; 
}; 

Derived<T> шаблон только инстанцирован при попытке создать экземпляр первого func() перегрузки, так почему же я до сих пор получаю ошибки даже для модифицированной версии? Вот пример:

$ 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 Спасибо за комментарий, представляющий меня жесткие и мягкие ошибки. Но мне нужна ссылка на http://stackoverflow.com/questions/15260685, предоставленную Jarod42 в его ответе, чтобы получить контекст. –

ответ

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, о котором я забыл упомянуть (minor: можно переписать). 2. ему нужна подпись участника, которой другое решение не требуется. Но ссылка на http://stackoverflow.com/questions/15260685 была действительно полезна, в основном, отвечая на мой вопрос: экземпляр 'Derived ' происходит на первом этапе создания экземпляра 'func ', где SFINAE не применяется. –

+0

Для * 2. *, я исправил свою реализацию. (поскольку подпись для 'T :: foo' не проверяется). – Jarod42

+0

спасибо, что я использовал вашу технику именно для этого случая. –

Смежные вопросы