2016-04-19 20 views
8

Vorrei verificare se una variabile membro di una classe è statica o meno. L'utilizzo di std :: is_member_pointer funziona correttamente per tutti i tipi ad eccezione dei membri di riferimento.Tipo tratto: controlla se la variabile membro di riferimento è statica o meno

#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.

comprendo l'errore, che un puntatore non può puntare ad un organo di riferimento. Ma come evitarlo e ancora distinguere se si tratta di una variabile statica o non statica? Qualche idea su questo?

+1

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

risposta

2

Si potrebbe aggiungere un ripiego in caso &E::foo non è riuscito utilizzando SFINAE (e un altro nel caso in cui E::foo non esiste affatto):

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

cosa fa questo codice:

  • Se &T::foo è valido, controllerà se il membro è statico o non usa std::is_member_pointer (la tua versione).
  • Se &T::foo non è valida, spetta alla seconda sovraccarico (qui si è certi che foo non è statica, o il primo di sovraccarico sarebbe stato scelto):
    • Se T::foo è valido (esiste un membro), restituisce std::true_type.
    • Otherwize si ripresenta all'ultimo sovraccarico e restituisce std::false_type.

noti inoltre (grazie a @iammilind) che per private membro, T::foo non è valido , quindi il terzo sovraccarico sarà scelto.

esempio Lavorando su Ideone: http://ideone.com/FILHbK

note a margine (spiegazione estesa):

  • Quando &T::foo è valido, i due primi sovraccarichi sono validi, ma il primo è scelto dal int è un esatto partita mentre long non lo è.
  • decltype(T::foo, std::true_type{}): T::foo: è qui solo per "lasciare che SFINAE" ricada al terzo sovraccarico se T::foo non è valido, ma il tipo risultante è std::true_type grazie all'operatore virgola.

Se si desidera, è possibile anche creare una versione generica (http://ideone.com/lzH2FB):

#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"); 

vedere questi due risposte, se si vuole incapsulare tutto in una classe anche (senza dover is_member_ funzioni) :

+0

La risposta mi sembra corretta. Lo ha svalutato. Puoi apportare alcune modifiche nel codice di esempio? 1. 'C' si ripete due volte, ma' D' non c'è. 2. Sostituisci 'typename C' con' typename T' poiché crea confusione per i lettori (esiste già una 'classe C'). Sia nel codice di esempio che nella tua risposta. A proposito, la tua comprensione di SFINAE è abbastanza buona e invece di creare i codici stand-alone come sopra, dovresti creare una libreria come il codice usando la macro. Metti anche nota che, potrebbe non funzionare per le variabili 'private'. – iammilind

+0

@iammilind Grazie, ho aggiornato la risposta e il codice ideone. – Holt

+0

Il tuo nuovo modo di creare un membro generico è buono. Ma immaginatelo, finirà per creare un sacco di 'is_member _ ## MEM' a causa della sua esclusività. Quindi dovresti pensare a un modo, in cui puoi incapsulare in una classe e continuare a utilizzare la stessa. Tecnicamente non c'è nulla di sbagliato, anche se si creano così tanti di tali modelli. Ma semplicemente non c'è bisogno di farlo. Il vantaggio collaterale di 'class' è che, puoi avere un' valore bool ', che può essere usato anche per scopi di stampa/controllo. – iammilind