2016-04-19 5 views
8

Ich möchte überprüfen, ob eine Elementvariable einer Klasse statisch ist oder nicht. Mit std :: is_member_pointer funktioniert für alle Typen mit Ausnahme von Referenzelementen.Typ Merkmal: Überprüfen Sie, ob Referenzelementvariable statisch ist oder nicht

#include <type_traits> 

struct A { 
    int foo; 
}; 

struct B : A {}; 

struct C { 
    static int foo; 
}; 

struct D : C { 
}; 

struct E { 
    int &foo; 
}; 

struct F { 
    static int &foo; 
}; 

static_assert(std::is_member_pointer<decltype(&A::foo)>::value, "No"); 
static_assert(std::is_member_pointer<decltype(&B::foo)>::value, "No"); 
static_assert(!std::is_member_pointer<decltype(&C::foo)>::value, "No"); 
static_assert(!std::is_member_pointer<decltype(&D::foo)>::value, "No"); 

// Fail to compile: 
static_assert(std::is_member_pointer<decltype(&E::foo)>::value, "No"); 

static_assert(!std::is_member_pointer<decltype(&F::foo)>::value, "No"); 

Live example.

Ich verstehe, den Fehler, dass ein Zeiger nicht auf ein Referenzelement zeigen kann. Aber wie kann man es vermeiden und trotzdem unterscheiden, ob es sich um eine statische oder nicht statische Variable handelt? Irgendeine Idee dazu?

+1

Verbunden: http://stackoverflow.com/questions/8336274/pointer-to-member-that-is-a-reference-illegal – PiotrNycz

Antwort

2

Sie einen Rückfall in Fall könnte hinzufügen &E::foo reißt mit SFINAE (und einem weiteren bei E::foo existiert nicht):

template <typename T> 
std::is_member_pointer<decltype(&T::foo)> is_member_foo(int); 

template <typename T> 
decltype(T::foo, std::true_type{}) is_member_foo(long); 

template <typename T> 
std::false_type is_member_foo(...); 

template <typename T> 
using IsMemberFoo = decltype(is_member_foo<T>(0)); 

static_assert(IsMemberFoo<A>{}, "No"); 
static_assert(IsMemberFoo<B>{}, "No"); 
static_assert(!IsMemberFoo<C>{}, "No"); 
static_assert(!IsMemberFoo<D>{}, "No"); 
static_assert(IsMemberFoo<E>{}, "No"); 
static_assert(!IsMemberFoo<F>{}, "No"); 
static_assert(!IsMemberFoo<G>{}, "No"); // struct G { }; 

Dieser Code macht:

  • Wenn &T::foo ist gültig, es wird überprüft, ob das Mitglied statisch ist oder nicht std::is_member_pointer (Ihre Version) verwendet.
  • Wenn &T::foo nicht gültig ist, fällt es zurück auf die zweite Überlast (hier Sie sicher sind, dass foo nicht statisch ist, oder die erste Überlast gewählt worden wäre):
    • Wenn T::foo gültig ist (ein Mitglied vorhanden), gibt es std::true_type zurück.
    • Otherwize fällt auf die letzte Überlastung zurück und gibt std::false_type zurück.

Beachten Sie auch (dank @iammilind), dass für private Mitglied, T::foo ist nicht gültig, so dass die dritte Überlastung gewählt werden.

Arbeitsbeispiel auf ideone: http://ideone.com/FILHbK

Side Noten (erweiterte Erklärung):

  • Wenn &T::foo gültig ist, sind die beiden ersten Überlastungen sind gültig, aber der erste, der gewählt wird, da int eine exakte ist passen, während long nicht ist.
  • decltype(T::foo, std::true_type{}): T::foo ist hier nur zu „let SFINAE“ zurück in der dritten Überlastung fallen, wenn T::foo nicht gültig ist, aber der resultierende Typ ist std::true_type dank des Komma-Operator.

Wenn Sie möchten, können Sie auch eine generic Version (http://ideone.com/lzH2FB) erstellen:

#define IsMember(MEM) \ 
template <typename T> \ 
std::is_member_pointer<decltype(&T::MEM)> is_member_##MEM(int); \ 
template<typename T> \ 
decltype(T::MEM, std::true_type{}) is_member_##MEM(long); \ 
template <typename T> \ 
std::false_type is_member_##MEM(...); \ 
template <typename T> \ 
using IsMember_##MEM = decltype(is_member_##MEM<T>(0)); 

// Instanciate IsMember_foo 
IsMember(foo); 

// Use it: 
static_assert(IsMember_foo<A>{}, "No"); 

auch diese beiden Antworten sehen, wenn Sie alles in einer Klasse kapseln wollen (ohne is_member_ Funktionen aufweisen) :

+0

Ihre Antwort scheint mir richtig. Upvoted es. Können Sie im Beispielcode einige Änderungen vornehmen? 1. 'C' wiederholt sich zweimal, aber' D' ist nicht da. 2. Ersetzen Sie "typename C" durch "typename T", da dies Verwirrung für die Leser schafft (es gibt bereits eine "Klasse C"). Sowohl im Beispielcode als auch in Ihrer Antwort. BTW, Ihr SFINAE-Verständnis ist ziemlich gut und anstatt die Stand-alone-Codes wie oben zu erstellen, sollten Sie eine Bibliothek wie Code mit Makro erstellen. Beachten Sie auch, dass es möglicherweise nicht für 'private' Variablen funktioniert. – iammilind

+0

@iammilind Danke, ich habe die Antwort und den Ideone-Code aktualisiert. – Holt

+0

Ihre neue Möglichkeit, generische Member zu erstellen, ist gut. Aber stell dir das vor, es wird wegen seiner Exklusivität eine Menge 'is_member _ ## MEM' erschaffen. Daher sollten Sie sich einen Weg überlegen, wo Sie in eine Klasse kapseln können und die selbe verwenden können. Technisch gesehen ist nichts falsch, auch wenn Sie so viele solcher Vorlagen erstellen. Aber das ist einfach nicht nötig. Der Nebenvorteil von 'class' ist, dass Sie einen' bool value' haben können, der auch zum Drucken/Prüfen verwendet werden kann. – iammilind