c++ c++11 reference typetraits

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

Ejemplo en vivo

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á usando std::is_member_pointer (su versión).
  • Si &T::foo no es válido, vuelve a la segunda sobrecarga (aquí está seguro de que foo no es estático, o se habría elegido la primera sobrecarga):
    • Si T::foo es válido (un miembro existe), devuelve std::true_type .
    • De otro modo, vuelve a la última sobrecarga y devuelve std::false_type .

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 que int es una coincidencia exacta, mientras que long no lo es.
  • decltype(T::foo, std::true_type{}) : T::foo solo está aquí para "dejar que SFINAE" retroceda a la tercera sobrecarga si T::foo no es válido, pero el tipo resultante es std::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