2012-04-26 3 views
5

Probleme mit SFINAE. Ich muss in der Lage sein zu bestimmen, ob ein Typ einen Mitgliedsfunktionsoperator definiert hat, unabhängig von seinem Rückgabetyp. Beispiel folgt.SFINAE - Es wird versucht, festzustellen, ob der Schablonentyp eine Mitgliedsfunktion mit der Rückgabetyp 'Variable' hat

Diese Klasse im Tester. Es definiert operator ->() mit einem Rückgabetyp von X *. Ich werde also nicht wissen, was "X" ist, um es überall zu kodieren.

template <class X> 
class PointerX 
{ 
    ... 

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

Zu dieser Klasse versucht wenn die in T weitergegeben bestimmt hat ein Verfahren operator-> definiert sind; unabhängig davon, welcher Operator-> Rückgabetyp ist.

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

Diese Klasse ist die gleiche wie oben, nur dass operator-> return type 'Object' sein muss.

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

Ergebnisse:

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 konnte PointX der Member-Funktion "Objekt Operator ->() const" finden. So verwendet es Test generische Version Test (...).

HasOperatorMemberAccessorOBJECT konnte jedoch den "Object Operator ->() const" von PointX finden. So verwendet es Test spezialisierte Version Test (SFINAE *).

Beide sollten in der Lage sein, den "Object operator ->() const" -Methode zu finden; und daher sollten beide die Testversion von Test (SFINAE *) verwenden; und daher sollte HasOperatorMemberAccessor> :: value für beide wahr sein.

Der einzige Unterschied zwischen HasOperatorMemberAccessor und HasOperatorMemberAccessorOBJECT ist, dass HasOperatorMemberAccessorOBJECT die Typnamen R hat, das Objekt fest einprogrammiert zu

So ist das Problem, dass "decltype (GetReturnType (& U :: operator->))" Rückkehr Objekt ist nicht korrekt. Ich habe eine Reihe verschiedener Genehmigungen ausprobiert, um den Rückgabetyp zu ermitteln. Sie gehen wie folgt:

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

Keine Arbeit, warum? Ich verwende MSVC++ 10.0.

+0

Eine Sache, die das Auge auffällt, ist, dass 'PointerX :: operator->' 'zurück bool *', 'nicht bool'. –

+0

Der Typ von X in PointerX spielt keine Rolle, was HasOperatorMemberAccessor betrifft. Ich habe versucht, mein Problem zu verallgemeinern, indem ich dem Beispiel nicht viele überflüssige Objekte hinzugefügt habe. Wenn es zu verwirrend ist, werde ich bool in string umwandeln. –

+0

Lassen Sie es mich erneut versuchen.PointerX :: operator-> 'returns' Objekt * '. 'declltype (...)' wäre 'Object *'. 'HasOperatorMemberAccessorOBJECT' scheint erfolgreich, obwohl es' declltype (...) 'durch' Object' ersetzt. Es sieht so aus, als wäre hier etwas nicht in Ordnung. Ich rede nicht über 'HasOperatorMemberAccessor' überhaupt. –

Antwort

4

Fragen Sie, wie Sie ein solches Merkmal implementieren, oder warum verhält sich decltype nicht wie erwartet? Wenn der ehemalige, hier ist ein Ansatz:

#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 fehlen die notwendigen C++ 11 Einrichtungen (zum Beispiel Ausdruck SFINAE) benötigt so viel sauberer zu machen.

+0

Nun, das hat ziemlich viel für mich funktioniert, außer dass es T nicht erlaubt, ein Zeigertyp zu sein.Ich änderte Basis: struct Base: :: Wert, T, no> bedingte :: Typ, base_mixin {}; Das heißt, ich würde gerne wissen, warum decltype arbeiten nicht. –

+0

@MichaelG: 'HasOperatorMemberAccessor <>' kann für Zeiger trivialer spezialisiert sein: 'template Klasse HasOperatorMemberAccessor : public std :: true_type {};'. Was 'decltype' nicht wie erwartet funktioniert, habe ich Ihren Code nicht viel untersuchen, aber VC++ 2010s' decltype' Implementierung ist Vornorm und Buggy oben drauf. – ildjarn

+0

@Michael: Bearbeitet mit richtiger Zeiger-Typ-Erkennung ('void *' war ein Fehler). – ildjarn