c++ - Tipo de rasgo: compruebe si la variable del miembro de referencia es estática o no
c++11 reference (1)
Me gustaría comprobar si una variable miembro de una clase es estática o no. El uso de std :: is_member_pointer funciona bien para todos los tipos, excepto para los miembros de referencia.
#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");
Entiendo el error, que un puntero no puede apuntar a un miembro de referencia. Pero, ¿cómo evitarlo y aún distinguir si se trata de una variable estática o no estática? Alguna idea sobre eso?
Podría agregar un respaldo en caso de que &E::foo
falle al usar SFINAE (y otro en caso de que E::foo
no exista):
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 { };
Lo que hace este código:
- Si
&T::foo
es válido, verificará si el miembro es estático o no está usandostd::is_member_pointer
(su versión). - Si
&T::foo
no es válido, vuelve a la segunda sobrecarga (aquí está seguro de quefoo
no es estático, o se habría elegido la primera sobrecarga):- Si
T::foo
es válido (un miembro existe), devuelvestd::true_type
. - De otro modo, vuelve a la última sobrecarga y devuelve
std::false_type
.
- Si
También tenga en cuenta (gracias a @iammilind) que para private
miembro private
, T::foo
no es válido , por lo que se elegirá la tercera sobrecarga.
Ejemplo de trabajo en ideone: http://ideone.com/FILHbK
Notas al margen (explicación extendida):
- Cuando
&T::foo
es válido, las dos primeras sobrecargas son válidas, pero se elige la primera, ya queint
es una coincidencia exacta, mientras quelong
no lo es. -
decltype(T::foo, std::true_type{})
:T::foo
solo está aquí para "dejar que SFINAE" retroceda a la tercera sobrecarga siT::foo
no es válido, pero el tipo resultante esstd::true_type
gracias al operador de coma.
Si lo desea, también puede crear una versión genérica ( 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");
También vea estas dos respuestas si desea encapsular todo en una clase (sin tener funciones is_member_
):
- https://.com/a/36694627/2666289
- https://.com/a/36693801/2666289