23 votos

La comprobación de un miembro existe, posiblemente en una clase base, C++11 versión

En http://stackoverflow.com/a/1967183/134841una solución es proporcionada por estáticamente la comprobación de si existe un miembro, posiblemente en una subclase del tipo:

template <typename Type> 
class has_resize_method
{ 
   class yes { char m;}; 
   class no { yes m[2];}; 
   struct BaseMixin 
   { 
     void resize(int){} 
   }; 
   struct Base : public Type, public BaseMixin {}; 
   template <typename T, T t>  class Helper{}; 
   template <typename U> 
   static no deduce(U*, Helper<void (BaseMixin::*)(), &U::foo>* = 0); 
   static yes deduce(...); 
public: 
   static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0))); 
};

Sin embargo, no funciona en C++11 final clases, porque se hereda de la clase bajo prueba, que final evita.

OTOH, este:

template <typename C>
struct has_reserve_method {
private:
    struct No {};
    struct Yes { No no[2]; };
    template <typename T, typename I, void(T::*)(I) > struct sfinae {};
    template <typename T> static No  check( ... );
    template <typename T> static Yes check( sfinae<T,int,   &T::reserve> * );
    template <typename T> static Yes check( sfinae<T,size_t,&T::reserve> * );
public:
    static const bool value = sizeof( check<C>(0) ) == sizeof( Yes ) ;
};

no puede encontrar la reserve(int/size_t) método en baseclasses.

Hay una implementación de este metafunction que ambos hallazgos reserved() en baseclasses de T y todavía funciona si T es final?

39voto

Matthieu M. Puntos 101624

En realidad, las cosas se pusieron mucho más fácil en C++11 gracias a la decltype y retraso en la devolución enlaces de maquinaria.

Ahora, es sólo simple de usar métodos para probar esto:

// Culled by SFINAE if reserve does not exist or is not accessible
template <typename T>
constexpr auto has_reserve_method(T& t) -> decltype(t.reserve(0), bool()) {
  return true;
}

// Used as fallback when SFINAE culls the template method
constexpr bool has_reserve_method(...) { return false; }

Usted puede utilizar esto en una clase, por ejemplo:

template <typename T, bool b>
struct Reserver {
  static void apply(T& t, size_t n) { t.reserve(n); }
};

template <typename T>
struct Reserver <T, false> {
  static void apply(T& t, size_t n) {}
};

Y usarla, por lo que:

template <typename T>
bool reserve(T& t, size_t n) {
  Reserver<T, has_reserve_method(t)>::apply(t, n);
  return has_reserve_method(t);
}

O usted puede elegir un enable_if método de:

template <typename T>
auto reserve(T& t, size_t n) -> typename std::enable_if<has_reserve_method(t), bool>::type {
  t.reserve(n);
  return true;
}

template <typename T>
auto reserve(T& t, size_t n) -> typename std::enable_if<not has_reserve_method(t), bool>::type {
  return false;
}

Tenga en cuenta que este tipo de maniobras cosas que en realidad no es tan fácil. En general, es mucho más fácil cuando sólo SFINAE existe, y sólo desea enable_if un método y no proporcionan ninguna reserva:

template <typename T>
auto reserve(T& t, size_t n) -> decltype(t.reserve(n), void()) {
  t.reserve(n);
}

Si la sustitución no, este método se elimina de la lista de posibles sobrecargas.

Nota: gracias a la semántica de , (el operador coma) se pueden encadenar varias expresiones en decltype y sólo el último decide realmente el tipo. Práctico para comprobar varias operaciones.

8voto

Luc Danton Puntos 21421

Una versión que también se basa en decltype , pero no en el paso de tipo arbitrario a (...) [ que es en realidad un no-problema, de todos modos, ver Johannes comentario']:

template<typename> struct Void { typedef void type; };

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

template<typename T>
struct has_reserve<
    T
    , typename Void<
        decltype( std::declval<T&>().reserve(0) )
    >::type
>: std::true_type {};

Me gustaría señalar de acuerdo a esta característica de un tipo como std::vector<int>& admite reserve: aquí las expresiones son inspeccionados, no tipos. La pregunta que este rasgo de la respuestas es "Dado un lvalue lval para el tipo de T, es el de las expresiones lval.reserve(0); bien formados". No idéntica, a la pregunta "¿este tipo o de cualquier de sus tipos base tiene un reserve miembro declarado".

Por otro lado, podría decirse que una característica! Recordar que el nuevo C++11 rasgo son del estilo is_default_constructible, no has_default_constructor. La distinción es sutil, pero tiene méritos. (La búsqueda de una mejor nombre apropiado en el estilo de is_*ible deja como ejercicio.)

En cualquier caso, usted puede todavía utilizar un rasgo, tal como std::is_class para lograr lo que desea.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X