Comprobando que existe un miembro, posiblemente en una clase base, versión C ++ 11

En https://stackoverflow.com/a/1967183/134841 , se proporciona una solución para comprobar estáticamente si un miembro existe, posiblemente en una subclase de un tipo:

template  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  class Helper{}; template  static no deduce(U*, Helper* = 0); static yes deduce(...); public: static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0))); }; 

Sin embargo, no funciona en las clases final C ++ 11, porque hereda de la clase bajo prueba, lo que previene el final .

OTOH, este:

 template  struct has_reserve_method { private: struct No {}; struct Yes { No no[2]; }; template  struct sfinae {}; template  static No check( ... ); template  static Yes check( sfinae * ); template  static Yes check( sfinae * ); public: static const bool value = sizeof( check(0) ) == sizeof( Yes ) ; }; 

no puede encontrar el método de reserve(int/size_t) en las clases base.

¿Hay una implementación de esta metafunción que ambos encuentren reserved() en clases base de T y aún funcione si T es final ?

En realidad, las cosas se pusieron mucho más fáciles en C ++ 11 gracias a la maquinaria de enlaces de decltype y retorno tardío.

Ahora, es simplemente más simple usar métodos para probar esto:

 // Culled by SFINAE if reserve does not exist or is not accessible template  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; } 

Puede usar esto en una clase, por ejemplo:

 template  struct Reserver { static void apply(T& t, size_t n) { t.reserve(n); } }; template  struct Reserver  { static void apply(T& t, size_t n) {} }; 

Y lo usas así:

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

O puede elegir un método enable_if :

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

Tenga en cuenta que este cambio de cosas en realidad no es tan fácil. En general, es mucho más fácil cuando solo existe SFINAE, y solo desea enable_if un método y no proporcionar ninguna alternativa:

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

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

Nota: gracias a la semántica de , (el operador de coma) puede encadenar múltiples expresiones en decltype y solo el último realmente decide el tipo. Práctico para verificar múltiples operaciones.

Una versión que también se basa en decltype pero no en pasar tipos arbitrarios a (...) [que de hecho no es un problema, ver el comentario de Johannes]:

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

Me gustaría señalar de acuerdo con este rasgo un tipo como std::vector& no admite reserve : aquí se inspeccionan las expresiones, no los tipos. La pregunta que responde este rasgo es “Dado un lval para tal tipo T , son las expresiones lval.reserve(0); well lval.reserve(0); “. No es idéntico a la pregunta “¿Este tipo o cualquiera de sus tipos básicos tiene un miembro de reserve declarado”.

Por otro lado, podría decirse que es una característica. Recuerde que el nuevo rasgo de C ++ 11 tiene el estilo is_default_constructible , no has_default_constructor . La distinción es sutil pero tiene méritos. (Encontrar un nombre que se ajuste mejor en el estilo de is_*ible dejó como ejercicio).

En cualquier caso, puede usar un rasgo como std::is_class para posiblemente lograr lo que desea.