2012-04-26 13 views
5

Tiene problemas con SFINAE. Necesito poder determinar si un Tipo tiene un operador de función miembro-> definido independientemente de su tipo de devolución. El ejemplo sigue.SFINAE - Tratando de determinar si el tipo de plantilla tiene función de miembro con el tipo de devolución 'variable'

Esta clase en el probador. Define operador ->() con un tipo de retorno de X *. Por lo tanto, no sabré qué 'X' es codificarlo en todos lados.

template <class X> 
class PointerX 
{ 
    ... 

    X* operator->() const; 
    ... 
} 

Esta clase intenta determinar si el pasado en T tiene un método operator-> defined; independientemente de qué operador-> tipo de retorno sea.

template<typename T> 
struct HasOperatorMemberAccessor 
{ 
    template <typename R, typename C> static R GetReturnType(R (C::*)()const); 

    template<typename U, typename R, R(U::*)()const> struct SFINAE{}; 
    template<typename U> static char Test(SFINAE<U,  decltype(GetReturnType(&U::operator->)), &U::operator-> >*); 
    template<typename U> static uint Test(...); 
    static const bool value = sizeof(Test<T>(0)) == sizeof(char); 
}; 

Esta clase es exactamente la misma que la anterior, excepto que operator-> return type tiene que ser 'Object'.

template<typename T> 
struct HasOperatorMemberAccessorOBJECT 
{ 
    template <typename R, typename C> static R GetReturnType(R (C::*)()const); 

    template<typename U, typename R, R(U::*)()const> struct SFINAE{}; 
    template<typename U> static char Test(SFINAE<U,  Object*,    &U::operator-> >*); // only change is we hardcoded Object as return type. 
    template<typename U> static uint Test(...); 
    static const bool value = sizeof(Test<T>(0)) == sizeof(char); 
}; 

Resultados:

void main() 
{ 
    HasOperatorMemberAccessor<PointerX<Object>>::Test<PointerX<Object>>(0);   // fails ::value is false; Test => Test(...) 

    HasOperatorMemberAccessorOBJECT<PointerX<Object>>::Test<PointerX<Object>>(0);  // works! ::value is true; Test => Test(SFINAE<>*) 
} 

HasOperatorMemberAccessor fue incapaz de encontrar la función miembro de pointx "operador de objetos ->() const". Entonces usa la versión genérica Test (...) de Test.

Sin embargo, HasOperatorMemberAccessorOBJECT pudo encontrar "Object operator ->() const" de PointX. Por lo tanto, utiliza Prueba de prueba versión especializada (SFINAE *).

Ambos deberían haber encontrado el método "Object operator ->() const"; y, por lo tanto, ambos deberían usar la versión especializada Test de Test (SFINAE *); y así HasOperatorMemberAccessor> :: value debería ser verdadero para ambos.

La única diferencia entre HasOperatorMemberAccessor y HasOperatorMemberAccessorOBJECT es que HasOperatorMemberAccessorOBJECT tiene el nombre de tipo R hardcoded a oponerse,

Así que la cuestión es que "decltype (GetReturnType (& T :: del operador>))" no está regresando de objetos correctamente. He intentado varias posibilidades diferentes de descubrir el tipo de devolución. Van de la siguiente manera:

decltype(GetReturnType(&U::operator->)) 
    typename decltype(GetReturnType(&U::operator->)) 
    decltype(((U*)nullptr)->operator->()) 
    typename decltype(((U*)nullptr)->operator->()) 

Ninguno funciona, ¿por qué? Estoy usando MSVC++ 10.0.

+0

Una cosa que llama la atención es que 'PointerX :: operator->' devuelve 'bool *', no 'bool'. –

+0

El tipo de X en PointerX no importa en lo que se refiere a HasOperatorMemberAccessor. Traté de generalizar mi problema al no agregar muchos objetos extraños al ejemplo. Si es demasiado confuso, cambiaré bool a cadena. –

+0

Déjame intentarlo de nuevo. 'PointerX :: operator->' devuelve 'Object *'. 'decltype (...)' sería 'Object *'. 'HasOperatorMemberAccessorOBJECT' aparentemente tiene éxito, aunque reemplaza' decltype (...) 'con' Object'. Parece que algo está mal aquí. No estoy hablando de 'HasOperatorMemberAccessor' en absoluto. –

Respuesta

4

¿Está preguntando cómo implementar dicho rasgo o por qué decltype no se comporta como esperaba? En el primer caso, aquí está uno de los enfoques:

#include <type_traits> 

template<typename T, bool DisableB = std::is_fundamental<T>::value> 
struct HasOperatorMemberAccessor 
{ 
private: 
    typedef char no; 
    struct yes { no m[2]; }; 

    struct ambiguator { char* operator ->() { return nullptr; } }; 
    struct combined : T, ambiguator { }; 
    static combined* make(); 

    template<typename U, U> struct check_impl; 
    template<typename U> 
    static no check(
     U*, 
     check_impl<char* (ambiguator::*)(), &U::operator ->>* = nullptr 
    ); 
    static yes check(...); 

public: 
    static bool const value=std::is_same<decltype(check(make())), yes>::value; 
}; 

// false for fundamental types, else the definition of combined will fail 
template<typename T> 
struct HasOperatorMemberAccessor<T, true> : std::false_type { }; 

// true for non-void pointers 
template<typename T> 
struct HasOperatorMemberAccessor<T*, false> : 
    std::integral_constant< 
     bool, 
     !std::is_same<typename std::remove_cv<T>::type, void>::value 
    > 
{ }; 

template<typename X> 
struct PointerX 
{ 
    X* operator ->() const { return nullptr; } 
}; 

struct X { }; 

int main() 
{ 
    static_assert(
     HasOperatorMemberAccessor<PointerX<bool>>::value, 
     "PointerX<> has operator->" 
    ); 
    static_assert(
     !HasOperatorMemberAccessor<X>::value, 
     "X has no operator->" 
    ); 
    static_assert(
     HasOperatorMemberAccessor<int*>::value, 
     "int* is dereferencable" 
    ); 
    static_assert(
     !HasOperatorMemberAccessor<int>::value, 
     "int is not dereferencable" 
    ); 
    static_assert(
     !HasOperatorMemberAccessor<void*>::value, 
     "void* is not dereferencable" 
    ); 
} 

VC++ 2010 carece de las necesarias instalaciones de C++ 11 (por ejemplo, la expresión SFINAE) necesarios para hacer esto mucho más limpio.

+0

Bueno, esto funcionó mucho para mí, excepto que no permite que T sea un tipo de puntero.Cambié la base a: struct base: condicional :: value, T, no> :: type, base_mixin {}; Dicho esto, me gustaría saber por qué decltype no funcionó. –

+0

@MichaelG: 'HasOperatorMemberAccessor <>' se puede especializar trivialmente para punteros: 'plantilla clase HasOperatorMemberAccessor : public std :: true_type {};'. En cuanto a 'decltype' que no funciona como esperabas, no investigué mucho tu código, pero la implementación de' decltype' de VC++ 2010 es pre-estándar y con fallas en la parte superior. – ildjarn

+0

@Michael: Editado con la detección de tipo de puntero adecuada ('void *' fue un gotcha). – ildjarn

Cuestiones relacionadas