2011-12-06 13 views
6

tengo la siguiente clase de plantilla y una variable (global) de su tipo:Detectar si un tipo se puede derivar de en C++

template <typename ClassT> 
struct ClassTester : public ClassT { 
    typedef ClassT type; 
}; 

ClassTester<int> *aaa; // No error here 

me esperaba un error de compilación porque int no se puede derivar de, pero este compila bien en Visual C++ 2010.

Si quito el puntero, me sale el error esperado compilación (int no se puede derivar de):

ClassTester<int> bbb; // Error here 

I w anted a utilizar esta clase para las pruebas SFINAE si el tipo dado es una clase que se puede derivar de:

template <typename T> 
struct CanBeDerivedFrom { 

    template <typename C> 
    static int test(ClassTester<T> *) { } 

    template <typename> 
    static char test(...) { } 

    static const bool value = (sizeof(test<T>(0)) == sizeof(int)); 
}; 

Esto, sin embargo, siempre informa cierto, incluso para tipos primitivos como int debido a la razón anterior. ¿Es este un comportamiento esperado/válido de C++?

+1

Sospecho que la razón es que simplemente declarando/definiendo un puntero no requiere realmente instanciar la plantilla. Después de todo, no usas 'ClassTester *' para nada. – visitor

+0

Un ejemplo similar sería comprobar si un parámetro de plantilla es POD o no POD. –

+0

Iba a sugerir como visitante. No creo que declarar un puntero instanciará la plantilla, por lo que puede compilarse. Intente acceder a un miembro utilizando la plantilla mediante el puntero, lo que obligaría al compilador a crear una instancia de la plantilla. De lo contrario, no estoy muy seguro, pero sé que no puedes heredar un tipo entero. – Jeremy

Respuesta

1

Creo que es no es posible en su totalidad para obtener un class que se puede derivar a través de SFINAE (que también incluye los casos de final class en C++ 11). Lo mejor que se puede hacer es tener un SFINAE para encontrar si un tipo es un class y confiar en eso.

template<typename T> 
struct void_ { typedef void type; }; 

template<typename T, typename = void> 
struct CanBeDerivedFrom { 
    static const bool value = false; 
}; 

template<typename T> 
struct CanBeDerivedFrom<T, typename void_<int T::*>::type> { 
    static const bool value = true; 
}; 

Este metaprograma encontrará si el tipo dado es class/union o no. demo.

+1

Aunque no es 100% la respuesta, definitivamente es un enfoque interesante. –

0

Puede usar RTTI (Información de tipo de tiempo de ejecución) para saber a qué tipo pertenece la clase, y si la clase es de tipo básico puede decir que no se puede derivar la clase.

Ex: si (typeid (T) == typeid (int) || typeid (T) == typeid (float)) { cout < < "La clase no se puede derivar de"; }

Puede añadir más tipos si se quiere, con la condición IF

+0

Gracias por su respuesta, pero lo necesito en tiempo de compilación, no en tiempo de ejecución. –

4

No reinventar la rueda. Use boost :: is_class boost reference manual

Esos tipos saben mejor que usted.

+2

boost :: is_class hace algo un poco diferente: prueba si el tipo dado es una clase. Quiero probar si se puede derivar el tipo. Estos dos no son los mismos en el nuevo estándar C++ 11. Y no he encontrado ningún rasgo de tipo Boost que me ayudaría a lograr esto. –

+2

Esta respuesta es muy grosera. – iammilind

+0

No creo que quisiera que fuera grosero. Está diciendo que BOOST ha investigado el tipo de cosas durante un tiempo, por lo que es mejor que utilices boost: is_class o al menos que veas cómo se implementó. – Jeremy

1

Iba a sugerir como visitante. No creo que declarar un puntero instanciará la plantilla, por lo que puede compilarse. Intente acceder a un miembro utilizando la plantilla mediante el puntero, lo que obligaría al compilador a crear una instancia de la plantilla. De lo contrario, no estoy muy seguro, pero sé que no puedes heredar un tipo entero.

Entonces, la respuesta que supongo sería que no es necesario, ya que el código probablemente no se compilará en caso de que intente crear una instancia de una clase de plantilla que hereda un tipo de entero. Puedo estar equivocado, pero creo que la única razón por la que se está compilando es porque la creación de un tipo de puntero no crea una instancia de la plantilla.

+0

Si intento acceder al puntero, produce un error de compilación que la clase no puede derivar de int. Eso también es extraño, pensé que SFINAE simplemente rechazaría esa sobrecarga y no arrojaría un error. –

0
#include <typeinfo> 

main() 
{ 
int i; 
int * pi; 
cout << int is: << typeid(int).name() << endl; 
cout << i is: << typeid(i).name() << endl; 
cout << pi is: << typeid(pi).name() << endl; 
cout << *pi is: << typeid(*pi).name() << endl << endl; 

}

imprime:

 

int 
int 
int* 
int 

como se esperaba ..

alguien necesita ser independiente dotado de otra biblioteca ... así impulsar la biblioteca no es una buena respuesta ..

2

Lamentablemente, creo que esto es realmente imposible.

Muchos problemas pueden evitar la derivación (o al menos, la derivación útil), la adición de final al estándar es uno.

Por ejemplo, vea this thread en la lista de correo de Clang donde Howard Hinnant requiere un compilador intrínseco para comprobar si la clase está marcada como final o no.

Cuestiones relacionadas