5

Supongamos que tenemos la siguiente clase de plantillaAyuda con características de tipo

template<typename T> class Wrap { /* ... */ }; 

Nos no podemos cambiarWrap. Es importante.

Deje que haya clases derivadas de Wrap<T>. Por ejemplo,

class NewInt : public Wrap<int>  { /* ... */ }; 
class MyClass : public Wrap<myclass> { /* ... */ }; 
class Foo  : public Wrap<Bar>  { /* ... */ }; 

Nos no puede cambiar estas clases también. Todas las clases anteriores son de terceros. Ellos no son míos

necesito el siguiente tiempo de compilación type_traits:

template<class T> 
struct is_derived_from_Wrap { 
    static const bool value = /* */; 
}; 

¿Qué necesito?

assert(is_derived_from_Wrap<Int>::value == true); // Indeed I need static assert 
assert(is_derived_from_Wrap<MyClass>::value == true); 
assert(is_derived_from_Wrap<char>::value == false); 
struct X {}; 
assert(is_derived_from_Wrap<X>::value == false); 
+0

¿Pero pueden cambiar 'Int' y' MyClass'? : p – kennytm

+0

No. Gracias por su pista. –

+2

¿No sería mejor denominar para los rasgos de su tipo: 'has_Wrap_for_base'? En realidad, MyClass no es una base de Wrap. –

Respuesta

9

Usted puede hacer esto utilizando SFINAE pero su tipo de mágica si usted no sabe lo que pasa ...

template<typename T> class Wrap { }; 

struct myclass {}; 
struct X {}; 

class Int  : public Wrap<int>  { /* ... */ }; 
class MyClass : public Wrap<myclass> { /* ... */ }; 

template< typename X > 
struct is_derived_from_Wrap 
{ 
    struct true_type { char _[1]; }; 
    struct false_type { char _[2]; }; 

    template< typename U > 
    static true_type test_sfinae(Wrap<U> * w); 
    static false_type test_sfinae(...); 

    enum { value = sizeof(test_sfinae((X*)(0)))==sizeof(true_type) }; 
}; 


#include <iostream> 
#define test(X,Y) std::cout<<(#X " == " #Y)<<" : "<<((X)?"true":"false") <<std::endl; 

int main() 
{ 
    test(is_derived_from_Wrap <Int>::value, true); 
    test(is_derived_from_Wrap <MyClass>::value, true); 
    test(is_derived_from_Wrap <char>::value, false); 
    test(is_derived_from_Wrap <X>::value, false); 
} 

Esto da el resultado esperado

is_derived_from_Wrap <Int>::value == true : true 
is_derived_from_Wrap <MyClass>::value == true : true 
is_derived_from_Wrap <char>::value == false : false 
is_derived_from_Wrap <X>::value == false : false 

Hay un par de trampas con mi código También volverá verdadero si el tipo es un Wrap.

assert( is_derived_from_Wrap< Wrap<char> >::value == 1); 

Esto probablemente se puede arreglar usando un poco más de magia SFINAE si es necesario.

devolverá false si la derivación no es una derivación pública (es decir, es privada o protegida)

struct Evil : private Wrap<T> { }; 
assert(is_derived_from_Wrap<Evil>::value == 0); 

Sospecho que esto no se puede arreglar. (Pero puedo estar equivocado). Pero sospecho que la herencia pública es suficiente.

+0

Supongo que esa especialización parcial simple como en mi código no funcionará? – sbi

+0

Corregir porque la especialización solo coincidirá si el tipo coincide exactamente. Su is_Wrap < Wrap> :: value = true, pero is_Wrap :: value = false. –

+0

Increíble. Solía ​​pensar que 'test_sfinae (Wrap * w);' no puede deducir 'U' para el caso de' X * '. –

0

A continuación se determina si algo es una envoltura:

template<class T> 
struct is_Wrap { static const bool value = false; }; 

template<typename T> 
struct is_Wrap< Wrap<T> > { static const bool value = true; }; 

Dado que la derivación es una relación es-un, todo derivado de Wrap<T> también es un Wrap<T> y debe ser encontrado por esto.

+0

Mi nombre 'base de Wrap' es incorrecto. Mi mal inglés. 'Derivado de la envoltura', por supuesto. –

+0

No es una solución muy buena. Entonces necesito hacer DEFINE para cada clase. No los conozco a todos. Quiero reconocer si X deriva de Wrap donde Y puede ser de cualquier tipo. –

+0

@Alexey: Trataré de adaptar mi código en consecuencia. Pero definitivamente necesita ser más preciso al hacer sus preguntas. (Y eso no es solo un problema de barrera de idioma.) – sbi

0

Es necesario realizar una metaprogramación de la plantilla bastante complicada para determinar si una clase X se deriva de otra Y, en el caso general. Básicamente X deriva de Y si:

  1. Y se puede convertir implícitamente a X
  2. X e Y no son del mismo tipo

Andrei Alexandrescu explica cómo hacer esto (junto con muchos otros trucos plantilla) en su libro "Modern C++ Design".

Puede encontrar el código que resuelve su problema ya sea en la biblioteca Loki o en la implementación uSTL, ambos escritos por Alexandrescu.

+1

Al volver a leer la pregunta, me doy cuenta de que mi sugerencia no resuelve el problema. No queremos saber si X deriva de Y, sino si X deriva de Y , donde T puede ser de cualquier tipo. –

+0

Sí. Tu respuesta no es solución. Pero te has dado cuenta del problema. –

+0

También podría citar boost :: is_base_of (que no trata el problema del OP ya que el parámetro Base no puede ser una plantilla de clase). –