2009-06-17 13 views
47

Para crear la función de plantilla de algoritmo, necesito saber si x o X (y y o Y) en la clase que es el argumento de la plantilla. Puede ser útil cuando uso mi función para MFC clase CPoint o GDI + clase PointF o algunos otros. Todos ellos usan diferentes x en ellos. Mi solución podría ser reduce al siguiente código:¿Cómo detectar si hay una variable de miembro específica en clase?


template<int> struct TT {typedef int type;}; 
template<class P> bool Check_x(P p, typename TT<sizeof(&P::x)>::type b = 0) { return true; } 
template<class P> bool Check_x(P p, typename TT<sizeof(&P::X)>::type b = 0) { return false; } 

struct P1 {int x; }; 
struct P2 {float X; }; 
// it also could be struct P3 {unknown_type X; }; 

int main() 
{ 
    P1 p1 = {1}; 
    P2 p2 = {1}; 

    Check_x(p1); // must return true 
    Check_x(p2); // must return false 

    return 0; 
} 

Pero no se compila en Visual Studio, durante la compilación en el GNU C++. Con Visual Studio que podría utilizar la siguiente plantilla:


template<class P> bool Check_x(P p, typename TT<&P::x==&P::x>::type b = 0) { return true; } 
template<class P> bool Check_x(P p, typename TT<&P::X==&P::X>::type b = 0) { return false; } 

Pero no se puede compilar en C++ de GNU. ¿Hay una solución universal?

UPD: Las estructuras P1 y P2 aquí son solo por ejemplo. Puede haber clases con miembros desconocidos.

P.S. Por favor, no publique soluciones de C++ 11 aquí porque son obvias y no son relevantes para la pregunta.

+0

No creo que la segunda forma sea estándar (las expresiones constantes integrales no pueden usar op == con operandos invo lving op &). Pero la primera forma se ve bien. ¿Qué dice msvC++ al respecto? –

+0

@litb: Eche un vistazo al enlace al final de mi respuesta: creo que eso explica el problema (por qué los compiladores lo rechazan y si está realmente permitido por C++ 98 Standard). –

+1

+1: desafío interesante :-) –

Respuesta

44

Otra forma es ésta, que se basa en SFINAE for expressions también. Si la búsqueda de nombre da lugar a la ambigüedad, el compilador rechazará la plantilla

template<typename T> struct HasX { 
    struct Fallback { int x; }; // introduce member name "x" 
    struct Derived : T, Fallback { }; 

    template<typename C, C> struct ChT; 

    template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1]; 
    template<typename C> static char (&f(...))[2]; 

    static bool const value = sizeof(f<Derived>(0)) == 2; 
}; 

struct A { int x; }; 
struct B { int X; }; 

int main() { 
    std::cout << HasX<A>::value << std::endl; // 1 
    std::cout << HasX<B>::value << std::endl; // 0 
} 

Se basa en una idea brillante de alguien en descarga directa.

Nota: HasX busca cualquier elemento de datos o funciones llamado x, con tipo arbitrario. El único propósito de introducir el nombre de miembro es tener una posible ambigüedad para la búsqueda de nombre de miembro; el tipo de miembro no es importante.

+0

Al principio no entendí la idea. Eso es exactamente lo que necesitaba. Esta solución funciona tanto en MSVC++ 2008 como en g ++ 4.2.4. –

+0

@litb: ¿Tiene un enlace a la discusión de Usenet? –

+3

sí, contenga la respiración: http://groups.google.com/group/comp.lang.c++.moderado/tree/browse_frm/thread/4f7c7a96f9afbe44/c95a7b4c645e449f? pli = 1 (vinculado desde el subproceso SO que también vincula por un chico). No te preocupes si no obtienes de inmediato el código de ese tipo. Es muy inteligente y me llevó bastante tiempo también –

2

¿Por qué no se utiliza la especialización de esta manera:

struct P1 {int x; }; 
struct P2 {int X; }; 

template<class P> 
bool Check_x(P p) { return true; } 

template<> 
bool Check_x<P2>(P2 p) { return false; } 
+0

Porque realmente no sé que struct P2 contiene grandes X y P1 contiene pequeñas x. Estas estructuras aquí solo por ejemplo. Puede haber estucos o clases. –

+0

Entonces no veo ninguna forma de reconocerlo usando plantillas (puede ser que estoy equivocado). Si los tipos de datos de x eran diferentes en P1 y P2, entonces podríamos haber usado sizeof para devolver verdadero o falso. – Naveen

+0

En mi pregunta, está el camino (en realidad, hay dos formas diferentes). Pero no sé cómo reconocerlo en ambos compiladores. –

1

¿Por qué no basta con crear especializaciones plantilla de Check_x?

template<> bool Check_x(P1 p) { return true; } 
template<> bool Check_x(P2 p) { return false; } 

Diablos, cuando pienso en ello. Si solo tiene dos tipos, ¿por qué necesita plantillas para esto?

+0

Hay más de dos tipos. Mira mi comentario a la respuesta de Naveen. –

2

La segunda respuesta (de litb) a esta muestra cómo detectar un miembro de:

Is it possible to write a template to check for a function's existence?

+0

Su respuesta no es aplicable a mi pregunta porque no conozco el tipo de miembro x (o X). Puede ser int o float o cualquier otra cosa (incluidos los tipos de usuario con operaciones definidas + - * /). –

1

son las funciones (x, x, y, Y) de una clase base abstracta, o podrían ser refactorizado para ser así? Si es así, puede utilizar la macro SUPERSUBCLASS() de Modern C++ Diseño, junto con las ideas de la respuesta a esta pregunta:

Compile-time type based dispatch

4

Boost.ConceptTraits proporciona entre otros algunos macros para definir características de tipo, como por ejemplo BOOST_TT_EXT_DEFINE_HAS_MEMBER(name), que define un rasgo tipo de la forma:

has_member_##name<T> 

Esto da cierto si T tiene un tipo de miembro de nombrado. Tenga en cuenta, sin embargo, que esto no detectará los miembros del tipo de referencia.

En le caso será suficiente para añadir en un archivo de cabecera

BOOST_TT_EXT_DEFINE_HAS_MEMBER_TYPE(x) 

y comprobar de la siguiente manera

BOOST_STATIC_ASSERT(has_member_x<P1>::value); 

La técnica utilizada es la misma que la explicada en algunos de los anteriores respuestas

Desafortunadamente esta biblioteca ya no se mantiene. Ahora que C++ 0x no incluye el concepto, esta biblioteca junto con SFINAE es un reemplazo perfecto para trabajar con la mayoría de los conceptos.

+1

Los Rasgos de concepto no se mantienen más porque, mientras que esa función evolucionó en estos dos: http://www.boost.org/doc/libs/1_42_0/libs/mpl/doc/refmanual /introspection.html otras funciones entraron en Concepts_checks, por ejemplo: http://www.boost.org/doc/libs/1_42_0/libs/concept_check/reference.htm#iterator-archetype – Blaisorblade

+0

Tiene razón. has_xxx ya está en Boost y responde a la pregunta. No estoy de acuerdo con el segundo enlace ya que los arquetipos y los conceptos son los dos lados de la misma moneda. –

7

ACTUALIZACIÓN: Recientemente he hecho algo más con el código que publiqué en mi respuesta original, así que estoy actualizando esto para dar cuenta de los cambios/adiciones.

Éstos son algunos fragmentos de uso: * Las tripas de todo esto son más abajo

Comprobar si x miembro de una clase dada. Podría ser var, func, clase, unión o enumeración:

CREATE_MEMBER_CHECK(x); 
bool has_x = has_member_x<class_to_check_for_x>::value; 

Compruebe si la función miembro void x():

//Func signature MUST have T as template variable here... simpler this way :\ 
CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x); 
bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value; 

Compruebe si hay variable miembro x:

CREATE_MEMBER_VAR_CHECK(x); 
bool has_var_x = has_member_var_x<class_to_check_for_x>::value; 

Buscar miembro clase x:

CREATE_MEMBER_CLASS_CHECK(x); 
bool has_class_x = has_member_class_x<class_to_check_for_x>::value; 

Compruebe si hay unión miembro de x:

CREATE_MEMBER_UNION_CHECK(x); 
bool has_union_x = has_member_union_x<class_to_check_for_x>::value; 

Compruebe si hay miembro de la enumeración x:

CREATE_MEMBER_ENUM_CHECK(x); 
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value; 

Compruebe si existe alguna función miembro x en cuenta la firma:

CREATE_MEMBER_CHECK(x); 
CREATE_MEMBER_VAR_CHECK(x); 
CREATE_MEMBER_CLASS_CHECK(x); 
CREATE_MEMBER_UNION_CHECK(x); 
CREATE_MEMBER_ENUM_CHECK(x); 
CREATE_MEMBER_FUNC_CHECK(x); 
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value; 

O

CREATE_MEMBER_CHECKS(x); //Just stamps out the same macro calls as above. 
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value; 

Detalles y núcleo:

/* 
    - Multiple inheritance forces ambiguity of member names. 
    - SFINAE is used to make aliases to member names. 
    - Expression SFINAE is used in just one generic has_member that can accept 
     any alias we pass it. 
*/ 

template <typename... Args> struct ambiguate : public Args... {}; 

template<typename A, typename = void> 
struct got_type : std::false_type {}; 

template<typename A> 
struct got_type<A> : std::true_type { 
    typedef A type; 
}; 

template<typename T, T> 
struct sig_check : std::true_type {}; 

template<typename Alias, typename AmbiguitySeed> 
struct has_member { 
    template<typename C> static char ((&f(decltype(&C::value))))[1]; 
    template<typename C> static char ((&f(...)))[2]; 

    //Make sure the member name is consistently spelled the same. 
    static_assert(
     (sizeof(f<AmbiguitySeed>(0)) == 1) 
     , "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified." 
    ); 

    static bool const value = sizeof(f<Alias>(0)) == 2; 
}; 

macros (El Diablo!):

CREATE_MEMBER_CHECK:

//Check for any member with given name, whether var, func, class, union, enum. 
#define CREATE_MEMBER_CHECK(member)           \ 
                      \ 
template<typename T, typename = std::true_type>        \ 
struct Alias_##member;              \ 
                      \ 
template<typename T>              \ 
struct Alias_##member <              \ 
    T, std::integral_constant<bool, got_type<decltype(&T::member)>::value> \ 
> { static const decltype(&T::member) value; };        \ 
                      \ 
struct AmbiguitySeed_##member { char member; };        \ 
                      \ 
template<typename T>              \ 
struct has_member_##member {            \ 
    static const bool value             \ 
     = has_member<              \ 
      Alias_##member<ambiguate<T, AmbiguitySeed_##member>>   \ 
      , Alias_##member<AmbiguitySeed_##member>      \ 
     >::value               \ 
    ;                  \ 
} 

CREATE_MEMBER_VAR_CHECK:

//Check for member variable with given name. 
#define CREATE_MEMBER_VAR_CHECK(var_name)         \ 
                      \ 
template<typename T, typename = std::true_type>        \ 
struct has_member_var_##var_name : std::false_type {};      \ 
                      \ 
template<typename T>              \ 
struct has_member_var_##var_name<           \ 
    T                  \ 
    , std::integral_constant<            \ 
     bool                \ 
     , !std::is_member_function_pointer<decltype(&T::var_name)>::value \ 
    >                  \ 
> : std::true_type {} 

CREATE_MEMBER_FUNC_SIG_CHECK:

//Check for member function with given name AND signature. 
#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix) \ 
                      \ 
template<typename T, typename = std::true_type>        \ 
struct has_member_func_##templ_postfix : std::false_type {};    \ 
                      \ 
template<typename T>              \ 
struct has_member_func_##templ_postfix<          \ 
    T, std::integral_constant<            \ 
     bool                \ 
     , sig_check<func_sig, &T::func_name>::value       \ 
    >                  \ 
> : std::true_type {} 

CREATE_MEMBER_CLASS_CHECK:

//Check for member class with given name. 
#define CREATE_MEMBER_CLASS_CHECK(class_name)    \ 
                  \ 
template<typename T, typename = std::true_type>    \ 
struct has_member_class_##class_name : std::false_type {}; \ 
                  \ 
template<typename T>          \ 
struct has_member_class_##class_name<      \ 
    T              \ 
    , std::integral_constant<        \ 
     bool            \ 
     , std::is_class<         \ 
      typename got_type<typename T::class_name>::type \ 
     >::value           \ 
    >              \ 
> : std::true_type {} 

CREATE_MEMBER_UNION_CHECK:

//Check for member union with given name. 
#define CREATE_MEMBER_UNION_CHECK(union_name)    \ 
                  \ 
template<typename T, typename = std::true_type>    \ 
struct has_member_union_##union_name : std::false_type {}; \ 
                  \ 
template<typename T>          \ 
struct has_member_union_##union_name<      \ 
    T              \ 
    , std::integral_constant<        \ 
     bool            \ 
     , std::is_union<         \ 
      typename got_type<typename T::union_name>::type \ 
     >::value           \ 
    >              \ 
> : std::true_type {} 

CREATE_MEMBER_ENUM_CHECK:

//Check for member enum with given name. 
#define CREATE_MEMBER_ENUM_CHECK(enum_name)     \ 
                  \ 
template<typename T, typename = std::true_type>    \ 
struct has_member_enum_##enum_name : std::false_type {}; \ 
                  \ 
template<typename T>          \ 
struct has_member_enum_##enum_name<       \ 
    T              \ 
    , std::integral_constant<        \ 
     bool            \ 
     , std::is_enum<          \ 
      typename got_type<typename T::enum_name>::type \ 
     >::value           \ 
    >              \ 
> : std::true_type {} 

CREATE_MEMBER_FUNC_CHECK:

//Check for function with given name, any signature. 
#define CREATE_MEMBER_FUNC_CHECK(func)   \ 
template<typename T>       \ 
struct has_member_func_##func {     \ 
    static const bool value      \ 
     = has_member_##func<T>::value   \ 
     && !has_member_var_##func<T>::value  \ 
     && !has_member_class_##func<T>::value \ 
     && !has_member_union_##func<T>::value \ 
     && !has_member_enum_##func<T>::value \ 
    ;           \ 
} 

CREATE_MEMBER_CHECKS:

//Create all the checks for one member. Does NOT include func sig checks. 
#define CREATE_MEMBER_CHECKS(member) \ 
CREATE_MEMBER_CHECK(member);   \ 
CREATE_MEMBER_VAR_CHECK(member);  \ 
CREATE_MEMBER_CLASS_CHECK(member);  \ 
CREATE_MEMBER_UNION_CHECK(member);  \ 
CREATE_MEMBER_ENUM_CHECK(member);  \ 
CREATE_MEMBER_FUNC_CHECK(member) 
+0

Las plantillas variables no son válidas en el estándar actual de C++. –

+0

Tal vez sea importante señalar que esto no funciona para los métodos heredados ... pero tal vez sea su intención. Supongo que std :: integral_constant es la limitación aquí. – TomSmartBishop

26

me redirigido aquí de un question que se ha cerrado como un duplicado de éste. Sé que es un hilo antiguo, pero solo quería sugerir una implementación alternativa (¿más simple?) Que funcione con C++ 11. Suponiendo que queremos comprobar si una determinada clase tiene una variable miembro llamada id:

#include <type_traits> 

template<typename T, typename = void> 
struct has_id : std::false_type { }; 

template<typename T> 
struct has_id<T, decltype(std::declval<T>().id, void())> : std::true_type { }; 

eso es todo. Y aquí es cómo se puede utilizar (live example):

#include <iostream> 

using namespace std; 

struct X { int id; }; 
struct Y { int foo; }; 

int main() 
{ 
    cout << boolalpha; 
    cout << has_id<X>::value << endl; 
    cout << has_id<Y>::value << endl; 
} 

Las cosas se pueden hacer aún más simple con un par de macros:

#define DEFINE_MEMBER_CHECKER(member) \ 
    template<typename T, typename V = bool> \ 
    struct has_ ## member : false_type { }; \ 
    template<typename T> \ 
    struct has_ ## member<T, \ 
     typename enable_if< \ 
      !is_same<decltype(declval<T>().member), void>::value, \ 
      bool \ 
      >::type \ 
     > : true_type { }; 

#define HAS_MEMBER(C, member) \ 
    has_ ## member<C>::value 

que podría ser utilizado de esta manera:

using namespace std; 

struct X { int id; }; 
struct Y { int foo; }; 

DEFINE_MEMBER_CHECKER(foo) 

int main() 
{ 
    cout << boolalpha; 
    cout << HAS_MEMBER(X, foo) << endl; 
    cout << HAS_MEMBER(Y, foo) << endl; 
} 
+0

Una buena solución, sin embargo, se encontrará con problemas si X tiene un ID de variable de miembro privado. – Xoph

50

Aquí hay una solución más simple que Johannes Schaub - litb 's one. Requiere C++ 11.

#include <type_traits> 

template <typename T, typename = int> 
struct HasX : std::false_type { }; 

template <typename T> 
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { }; 

actualización: Un ejemplo rápido y la explicación de cómo funciona esto.

Para estos tipos:

struct A { int x; }; 
struct B { int y; }; 

tenemos HasX<A>::value == true y HasX<B>::value == false. Veamos por qué.

En primer lugar recordemos que std::false_type y std::true_type tienen un miembro static constexpr bool llamado value que se fija para false y true, respectivamente. Por lo tanto, las dos plantillas HasX anteriores heredan este miembro. (La primera plantilla de std::false_type y la segunda de std::true_type.)

Comencemos simple y luego procedamos paso a paso hasta que lleguemos al código anterior.

1) Punto de partida:

template <typename T, typename U> 
struct HasX : std::false_type { }; 

En este caso, no es ninguna sorpresa: HasX deriva de std::false_type y por lo tanto HasX<bool, double>::value == false y HasX<bool, int>::value == false.

2) Incumplidor U:

// Primary template 
template <typename T, typename U = int> 
struct HasX : std::false_type { }; 

Dado que U valores predeterminados para int, Has<bool> significa en realidad HasX<bool, int> y por lo tanto, HasX<bool>::value == HasX<bool, int>::value == false.

3) Adición de una especialización:

// Primary template 
template <typename T, typename U = int> 
struct HasX : std::false_type { }; 

// Specialization for U = int 
template <typename T> 
struct HasX<T, int> : std::true_type { }; 

En general, gracias a la plantilla primaria, HasX<T, U> deriva de std::false_type. Sin embargo, existe una especialización para U = int que deriva de std::true_type. Por lo tanto, HasX<bool, double>::value == false pero HasX<bool, int>::value == true.

Gracias a la configuración predeterminada para U, HasX<bool>::value == HasX<bool, int>::value == true.

4) decltype y una manera elegante de decir int:

Una pequeña digresión aquí, pero, por favor, ten paciencia conmigo.

Básicamente (esto no es del todo correcto), decltype(expression) produce el tipo de expresión . Por ejemplo, 0 tiene tipo int por lo tanto, decltype(0) significa int. Análogamente, 1.2 tiene el tipo double y por lo tanto, decltype(1.2) significa double.

Considérese una función con esta declaración:

char func(foo, int); 

donde foo es algún tipo de clase. Si f es un objeto de tipo foo, entonces decltype(func(f, 0)) significa char (el tipo devuelto por func(f, 0)).

Ahora, la expresión (1.2, 0) utiliza la (built-in) coma operador que evalúa los dos sub-expresiones en orden (es decir, primero 1.2 y luego 0), descarta el primer valor y los resultados en la segunda.Por lo tanto,

int x = (1.2, 0); 

es equivalente a

int x = 0; 

Poner esto junto con decltype da que decltype(1.2, 0) significa int. No hay nada realmente especial sobre 1.2 o double aquí. Por ejemplo, true tiene tipo bool y decltype(true, 0) significa int también.

¿Qué tal un tipo de clase? Por ejemplo, ¿qué significa decltype(f, 0)? Es natural esperar que esto todavía signifique int pero podría no ser el caso. De hecho, puede haber una sobrecarga para el operador de coma similar a la función func anterior que toma un foo y un int y devuelve un char. En este caso, decltype(foo, 0) es char.

¿Cómo podemos evitar el uso de una sobrecarga para el operador de coma? Bueno, no hay forma de sobrecargar al operador de coma para un operando void y podemos convertir cualquier cosa al void. Por lo tanto, decltype((void) f, 0) significa int. De hecho, (void) f arroja f de foo a void que básicamente no hace más que decir que la expresión debe considerarse como que tiene el tipo void. A continuación, se utiliza la coma incorporada del operador y ((void) f, 0) da como resultado 0 que tiene el tipo int. Por lo tanto, decltype((void) f, 0) significa int.

¿Este modelo es realmente necesario? Bueno, si no hay sobrecarga para el operador de coma tomando foo y int, entonces esto no es necesario. Siempre podemos inspeccionar el código fuente para ver si hay tal operador o no. Sin embargo, si esto aparece en una plantilla y f tiene el tipo V que es un parámetro de plantilla, entonces ya no está claro (o incluso es imposible saber) si existe dicha sobrecarga para el operador de coma o no. Para ser genéricos, elegimos de todos modos.

En pocas palabras: decltype((void) f, 0) es una manera elegante de decir int.

5) SFINAE:

Esta es una ciencia entera ;-) Aceptar que estoy exagerando, pero no es muy simple tampoco. Así que mantendré la explicación al mínimo.

SFINAE significa Insuficiencia de sustitución no es un error. Significa que cuando un parámetro de plantilla se sustituye por un tipo, puede aparecer un código C++ ilegal, pero en algunas circunstancias, en lugar de cancelar la compilación, el compilador simplemente ignora el código ofensivo como si no estuviera allí. Vamos a ver cómo se aplica a nuestro caso:

// Primary template 
template <typename T, typename U = int> 
struct HasX : std::false_type { }; 

// Specialization for U = int 
template <typename T> 
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { }; 

Aquí, de nuevo, decltype((void) T::x, 0) es una forma elegante de decir int pero con el beneficio de SFINAE.

Cuando T se sustituye por un tipo, puede que aparezca una construcción no válida. Por ejemplo, bool::x no es C++ válido, por lo que sustituir T con bool en T::x produce una construcción no válida.Según el principio SFINAE, el compilador no rechaza el código, simplemente lo ignora (en partes). Más precisamente, como hemos visto HasX<bool> significa realmente HasX<bool, int>. Se debe seleccionar la especialización para U = int pero, al crear una instancia, el compilador encuentra bool::x e ignora por completo la especialización de la plantilla como si no existiera.

En este punto, el código es esencialmente el mismo que en el caso (2) anterior donde solo existe la plantilla principal. Por lo tanto, HasX<bool, int>::value == false.

El mismo argumento utilizado para bool mantiene para B desde B::x es una construcción no válido (B tiene ningún miembro x). Sin embargo, A::x está bien y el compilador no ve ningún problema al crear una instancia de la especialización para U = int (o, más precisamente, para U = decltype((void) A::x, 0)). Por lo tanto, HasX<A>::value == true.

6) Unnaming U:

Bueno, mirando el código en (5) de nuevo, vemos que el nombre U no se utiliza en cualquier lugar pero en su declaración (typename U). A continuación, podemos anular el nombre del segundo argumento de plantilla y obtenemos el código que se muestra en la parte superior de esta publicación.

+0

Esto no parece funcionar: http://ideone.com/QDhC8A –

+0

@Jurak Blaho: El problema es GCC 4.7.2. Funciona con Clang 3.2 y IIRC también funciona con GCC 4.8. (Es una pena que [liveworkspace] (http://liveworkspace.org/) no haya funcionado por un tiempo). –

+0

¿Podría explicarnos un poco más cómo funciona esto, por favor? – DarioP

0

Podemos obtener en tiempo de compilación: 0 - not_member, 1 - is_object, 2 - is_function para cada clase y miembro requerido - objeto o función: http://ideone.com/Fjm9u5

#include <iostream> 
#include <type_traits> 

#define IS_MEMBER(T1, M) \ 
struct {  \ 
    struct verystrangename1 { bool M; }; \ 
    template<typename T> struct verystrangename2 : verystrangename1, public T { }; \ 
    \ 
    enum return_t { not_member, is_object, is_function }; \ 
    template<typename T, typename = decltype(verystrangename2<T>::M)> constexpr return_t what_member() { return not_member; } \ 
    template<typename T> typename std::enable_if<std::is_member_object_pointer<decltype(&T::M)>::value, return_t>::type constexpr what_member() { return is_object; } \ 
    template<typename T> typename std::enable_if<std::is_member_function_pointer<decltype(&T::M)>::value, return_t>::type constexpr what_member() { return is_function; } \ 
    constexpr operator return_t() { return what_member<T1>(); } \ 
} 

struct t { 
    int aaa; 
    float bbb; 
    void func() {} 
}; 

// Can't be in function 
IS_MEMBER(t, aaa) is_aaa_member_of_t; 
IS_MEMBER(t, ccc) is_ccc_member_of_t; 
IS_MEMBER(t, func) is_func_member_of_t; 

// known at compile time 
enum { const_is_aaa_member_of_t = (int)is_aaa_member_of_t }; 
static constexpr int const_is_func_member_of_t = is_func_member_of_t; 

int main() {   
    std::cout << std::boolalpha << "0 - not_member, 1 - is_object, 2 - is_function \n\n" << 
     "is aaa member of t = " << is_aaa_member_of_t << std::endl << 
     "is ccc member of t = " << is_ccc_member_of_t << std::endl << 
     "is func member of t = " << is_func_member_of_t << std::endl << 
     std::endl; 

    return 0; 
} 

Resultado:

0 - not_member, 1 - is_object, 2 - is_function 

is aaa member of t = 1 
is ccc member of t = 0 
is func member of t = 2 

Para la clase/estructura:

struct t { 
    int aaa; 
    float bbb; 
    void func() {} 
}; 
Cuestiones relacionadas