marți, 3 ianuarie 2012

Fata nevazuta a template-urilor. Partea 1 - Detectia mostenirilor

    Majoritatea celor ce ajung sa foloseasca template-uri o fac de cele mai multe ori datorita versatilitatii sectiunii de cod/clasa pe care au elaborato. Putini stiu insa fata nevazuta a template-urilor si posibilitatile lor. In acest scurt tutorial o sa va arat cum se face detectia la compilare a relatiilor de legatura/mostenire intre doua clase A si B.
         Se stie ca functia sizeof reuseste sa returneze dimensiunea oricat de complicata ar arata expresia ce ii este trimisa ca argument. Defapt nici nu evalueaza expresia, sa zicem ca pur si simplu foloseste  tipul atasat expresiei.  De aici putem conchide ca si compilatorul stie cate ceva si despre relatiile de mostenire dintre obiecte.
          Vom scrie o clasa  template, ce primeste cele doua tipuri de date si  decide daca exista o relatie de legatura intre cele doua. Mai exact returneaza existenta relatiei U->T.

template<class T, class U>
struct Conversion
{
    typedef char size1;
    typedef struct {
        char dummy[2];
    }size2;
    static T TType;
    static size1 Test(U);
    static size2 Test(...);
public:
    enum{exists = sizeof(Test(TType)) == sizeof(size1), sametype = false};
};
          Clasa declara doua tipuri care sa fie in mod evident diferite ca dimensiune: size1 si size2. Pe langa aceastea, clasa mai vine si cu doua functii Test: una generica ce foloseste puncte de suspensie si returneaza dimensiunea size2 si una explicita ce returneaza dimensiunea size1. Definitiile lor nu sunt necesare fiindca am zis ca functia sizeof " arunca" expresia si returneaza doar tipul. Acuma, daca tipul T este cu adevarat derivat din tipul U, compilatorul  va folosi cea mai calificata functie Test, adica functia Test(U) intrucat compilatorul "stie" ca se poate face downcast de la tipul T la tipul U.  Variabila exists se va dezvolta in sizeof(Test(U)) == sizeof(size1) care este un adevar evident.
         Pe langa asta va trebui sa ne asiguram si ca cele doua tipuri nu sunt identice pentru a putea vorbi de mostenire. Vom face o calificare partiala a clasei pentru situatia asta.

template<class T>
struct Conversion<T,T>
{
    enum{exists = 1, sametype = 1};
};

Iata si testul final:


struct A
{
    int dummy1;
};
struct B:public A
{
    int dummy2[3];
};

bool inheritance = Conversion<B,A>::exists && !Conversion<B,A>::sametype; // TRUE
inheritance = Conversion<B,size_t>::exists && !Conversion<B,A>::sametype; // FALSE

Niciun comentariu:

Trimiteți un comentariu