2010-04-13 14 views
16

Un collaboratore mi ha recentemente mostrato un codice che ha trovato online. Sembra consentire la determinazione del tempo di compilazione per stabilire se un tipo ha una relazione "è una" con un altro tipo. Penso che sia assolutamente fantastico, ma devo ammettere che non ho idea di come funzioni effettivamente. Qualcuno può spiegarmi questo?Determinazione del tipo di tempo di compilazione in 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 volta che questa funzione è definita, è possibile utilizzarlo in questo modo:

#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

Questo è dannatamente fresco voodoo. – zneak

+0

+1 Impressionante trucco. – SLaks

+7

Se sei interessato a queste cose, procurati una copia di Alexandrescus * "Modern C++ Design" *. –

risposta

11

Dichiara due funzioni sovraccariche denominate test, una che prende uno Base e una che prende qualsiasi cosa (...) e restituisce diversi tipi.

Quindi chiama la funzione con un Derived e controlla la dimensione del suo tipo di ritorno per vedere quale sovraccarico viene chiamato. (Si chiama effettivamente la funzione con il valore di ritorno di una funzione che restituisce Derived, per evitare di usare memoria)

Poiché enum s sono costanti in fase di compilazione, tutto questo viene fatto nel sistema di tipo a tempo di compilazione. Poiché le funzioni non finiscono per essere richiamate in fase di runtime, non importa che non abbiano corpi.

+0

'derived()' non è lì per il salvataggio della memoria - l'espressione 'sizeof() 'viene valutata comunque in fase di compilazione - è un modo per non richiedere' Derived' per essere costruttibili di default (cioè per evitare di usare 'test (Derived())'). –

+0

@ gf: intendevo invece di dichiarare un puntatore a un 'Dervied'. – SLaks

6

Non sono un C esperto ++, ma mi sembra il punto è quello di ottenere il compilatore di decidere tra i due sovraccarichi di test(). Se Derived deriva da Base, verrà utilizzato il primo, che restituisce char, altrimenti verrà utilizzato il secondo, che restituisce char[2]. L'operatore sizeof() determina quindi quale di questi è successo e imposta il valore di conversion::exists di conseguenza.

+0

true..its solo in base alla risoluzione di sovraccarico. – mukeshkumar

+0

bella spiegazione :) –

5

È piuttosto interessante, ma in realtà non funziona, perché la conversione definita dall'utente è preferibile alla corrispondenza ellissi e il riferimento const può associare il risultato temporaneo di una conversione definita dall'utente. Quindi char*p; is_related<bool>(p); restituirebbe true (testato in VS2010).

Se si desidera verificare realmente una relazione di ereditarietà, è possibile adottare un approccio simile ma utilizzando puntatori anziché riferimenti.

2

A proposito, è possibile utilizzare __is_base_of da "type_traits" introdotto in std::tr1 (il compilatore MSCV 2008 ha il supporto intrinseco per quello).

5

C'è qualche ragione non sarebbe usare qualcosa come questo, invece:

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)) }; 
} 

?

es .:

IsRelated<Base, Derived>::exists 

In questo modo si ha accesso alle informazioni al momento della compilazione.

1

Il codice originale costruirà un oggetto Derivato, potrebbe portare a risultati imprevisti. Sotto il lavoro può essere una scelta 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)); 
} 
Problemi correlati