2010-04-13 12 views
16

Un compañero de trabajo me mostró recientemente un código que encontró en línea. Parece permitir la determinación del tiempo de compilación de si un tipo tiene una relación "es una" con otro tipo. Creo que esto es totalmente increíble, pero tengo que admitir que no tengo ni idea de cómo funciona esto en realidad. ¿Puede alguien explicarme esto?Determinación del tipo de tiempo de compilación en C++

template<typename BaseT, typename DerivedT> 
inline bool isRelated(const DerivedT&) 
{ 
    DerivedT derived(); 
    char test(const BaseT&); // sizeof(test()) == sizeof(char) 
    char (&test(...))[2]; // sizeof(test()) == sizeof(char[2]) 
    struct conversion 
    { 
     enum { exists = (sizeof(test(derived())) == sizeof(char)) }; 
    }; 
    return conversion::exists; 
} 

Una vez definida esta función, se puede utilizar de esta manera:

#include <iostream> 

class base {}; 
class derived : public base {}; 
class unrelated {}; 

int main() 
{ 
    base b; 
    derived d; 
    unrelated u; 

    if(isRelated<base>(b)) 
     std::cout << "b is related to base" << std::endl; 

    if(isRelated<base>(d)) 
     std::cout << "d is related to base" << std::endl; 

    if(!isRelated<base>(u)) 
     std::cout << "u is not related to base" << std::endl; 
} 
+0

Eso es vudú bastante maldito. – zneak

+0

+1 truco impresionante. – SLaks

+7

Si está interesado en esas cosas, obtenga una copia de Alexandrescus * "Modern C++ Design" *. –

Respuesta

11

Declara dos funciones sobrecargadas llamadas test, una teniendo un Base y una tomando cualquier cosa (...), y devolviendo tipos diferentes.

Luego llama a la función con un Derived y comprueba el tamaño de su tipo de devolución para ver a qué sobrecarga se llama. (En realidad llama a la función con el valor de retorno de una función que devuelve Derived, evitar el uso de la memoria)

Debido enum s son constantes en tiempo de compilación, todo esto se hace dentro del sistema de tipos en tiempo de compilación. Como las funciones no terminan siendo llamadas en tiempo de ejecución, no importa que no tengan cuerpos.

+0

'derived()' no está ahí para guardar memoria - la expresión 'sizeof()' se evalúa en tiempo de compilación de todos modos - es una forma de no requerir que 'Derived' sea predecible (es decir, evite usar 'test (Derived())'). –

+0

@gf: quise decir a diferencia de declarar un puntero a un 'Dervied'. – SLaks

6

No soy C experto ++, pero parece a mí como el punto es conseguir que el compilador de decidir entre las dos sobrecargas de test(). Si Derived se deriva de Base, se utilizará la primera, que devuelve char; de lo contrario, se utilizará la segunda, que devuelve char[2]. El operador sizeof() determina cuál de estos sucedió y establece el valor de conversion::exists en consecuencia.

+0

true .. solo se basa en la resolución de sobrecarga. – mukeshkumar

+0

nice explanation :) –

5

Es bastante genial, pero en realidad no funciona, porque la conversión definida por el usuario es preferible a la coincidencia de puntos suspensivos, y la referencia de referencia puede vincular el resultado temporal de una conversión definida por el usuario. Entonces char*p; is_related<bool>(p); devolvería true (probado en VS2010).

Si realmente quiere probar una relación de herencia, se puede tomar un enfoque similar pero usando punteros en lugar de referencias.

2

Por cierto, puede usar __is_base_of desde "type_traits" introducido en std::tr1 (el compilador de MSCV 2008 tiene soporte intrínseco para eso).

5

¿Hay alguna razón por la que no utilizaría algo como esto en su lugar:

template<typename BaseT, typename DerivedT> 
struct IsRelated 
{ 
    static DerivedT derived(); 
    static char test(const BaseT&); // sizeof(test()) == sizeof(char) 
    static char (&test(...))[2]; // sizeof(test()) == sizeof(char[2]) 

    enum { exists = (sizeof(test(derived())) == sizeof(char)) }; 
} 

?

ej .:

IsRelated<Base, Derived>::exists 

De esta manera se tiene acceso a la información en tiempo de compilación.

1

El código original construirá un objeto Derivado, puede traer resultados inesperados. Debajo del trabajo puede haber una opción alternativa:

template<typename BaseT, typename CheckT> 
inline bool isDerived(const CheckT &t){ 
    char test(const BaseT *t); 
    char (&test(...))[2]; 
    return (sizeof(test(&t)) == sizeof(char)); 
} 
Cuestiones relacionadas