2011-07-22 12 views
15

Jestem początkującym programistą C++, ale pomyślałem, że wiem wystarczająco dużo o C++ do dzisiaj, kiedy natknąłem się na taki kod w pracy i nie mogłem zrozumieć, jak to działa .Szalony szablon C++ - szablon dostępu do poszczególnych atrybutów klasy

class Object 
{ 
}; 

template < 
     class PropObject, 
     class PropType, 
     PropType PropObject::* Prop 
     > 
class PropReader 
{ 
public: 
    void print(Object& o) 
    { 
     PropObject& po = static_cast<PropObject &>(o); 
     PropType& t = po.*Prop; 

     cout << t << "\n"; 
    } 
}; 

class Student : public Object 
{ 
public: 
    int age; 
    int grade; 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    Student s; 
    s.age = 10; 
    s.grade = 5; 

    PropReader<Student, int, &Student::age> r; 
    PropReader<Student, int, &Student::grade> r2; 

    r.print(s); 
    r2.print(s); 
} 

Myślę, że rozumiem na wysokim poziomie. Ale ta szczególna PropType PropObject::* Prop w deklaracji szablonu przeszkadza mi. Co to znaczy? Szukam wyjaśnienia od ekspertów C++. Chciałbym to zrozumieć, aby móc go lepiej wykorzystać. Wygląda jednak bardzo użytecznie.

Odpowiedz

18

Szablony C++ są dobrze znane z tego, że przyjmują typy jako argumenty, ale można je również sparametryzować w stosunku do innych typów danych. Na przykład, można templatize klasę na liczbę całkowitą, jak pokazano poniżej:

template <typename T, unsigned int N> class Array { 
private: 
    T array[N]; 

public: 
    /* ... */ 
}; 

Szablony mogą być również programowane przez wskaźniki, o ile wskaźnik spełnia określone kryteria (na przykład, to musi ocenić na adres czegoś, co można określić podczas kompilacji).Na przykład, jest to całkowicie legalne:

template <int* Pointer> class ThisIsLegal { 
public: 
    void doSomething() { 
     *Pointer = 137; 
    } 
}; 

W kodzie, szablon jest parametryzowane przez wskaźnik do klasy-członka. Wskaźnik do klasy jest podobny do wskaźnika, ponieważ pośrednio odwołuje się do jakiegoś obiektu. Jednak zamiast wskazywać na obiekt, wskazuje on pole w klasie. Pomysł polega na tym, że można wyłuskać wskaźnik do klasy członka względem jakiegoś obiektu, aby wybrać to pole z klasy. Oto prosty przykład wskaźników-do-klasowej użytkownika:

struct MyStruct { 
    int x, y; 
}; 

int main() { 
    MyStruct ms; 
    ms.x = 137; 
    ms.y = 42; 

    int MyStruct::* ptr; // Declare a pointer to a class member. 
    ptr = &MyStruct::x; // Now points to the field 'x' 

    ms.*ptr = 0;   // Set the 'x' field of ms to be zero. 
} 

zauważyć, że składnia deklarowania wskaźnik do klasy członek jest

Type ContainingClass::* pointerName; 

Tak więc w powyższym kodzie int MyStruct::* ptr pomocą „. wskaźnik do int wnętrza MyStruct klasa

w kodzie, które zostały zaksięgowane, deklaracja szablon brzmi tak:

template < 
    class PropObject, 
    class PropType, 
    PropType PropObject::* Prop 
    > 
class PropReader 

Zobaczmy, co to oznacza. Dwa pierwsze obiekty argumentu szablonu, których własność ma zostać odczytana, i PropType, typ tej właściwości. "Ostatnim argumentem do szablonu jest element wskaźnika do klasy o nazwie Prop, który wskazuje wewnątrz pola na wartość . wpisz PropType na przykład, można utworzyć wystąpienia tego szablonu z MyStruct jak ten:.

PropReader<MyStruct, int, &MyStruct::x> myPropReader; 

teraz zobaczmy co reszta kodu jest ciało tej klasy szablonu jest przedrukowany tutaj.

void print(Object& o) 
{ 
    PropObject& po = static_cast<PropObject &>(o); 
    PropType& t = po.*Prop; 

    cout << t << "\n"; 
} 

Niektóre z nich można dość łatwo odczytać. dla tej funkcji jest odniesieniem do Object o nazwie o, a ostatnia linia drukuje niektóre pola. Te dwie linie są trudne:

PropObject& po = static_cast<PropObject &>(o); 
PropType& t = po.*Prop; 

Ta pierwsza linia jest typecast że mówi „spróbuj rzucić argument o do odniesienia typu PropObject Pomysł, zgaduję, że Object pewne klasy bazowej. wielu różnych obiektów .Parametrem tej funkcji jest zwykły Object, a ta obsada próbuje przekonwertować ją na coś odpowiedniego typu (przypomnij, że PropObject jest szablonowym argumentem mówiącym o typie obiektu). to używa static_cast, jeśli konwersja nie jest zdefiniowana (na przykład próbujesz utworzyć instancję szablonu przez int lub vector<string>), kod nie zostanie skompilowany. e, kod ufa, że ​​rzutowanie jest bezpieczne, a następnie otrzymuje odniesienie typu PropObject do tego, do czego odnosi się parametr.

Wreszcie ostatnia linia jest

PropType& t = po.*Prop; 

używa wyłuskiwania składni wskaźnik do klasy-członka wspomniałem wcześniej, aby powiedzieć „wybierz pole wskazał przez Prop (argument szablonu), a następnie przechowywać odniesienie do niego o nazwie t.

Więc, krótko mówiąc, szablon

  1. prosi o rodzaju jakiegoś przedmiotu.
  2. Pyta o typ pola w tym obiekcie.
  3. Pyta o wskaźnik do pola w tym obiekcie.
  4. Udostępnia funkcję print, która w przypadku obiektu próbuje wydrukować to pole.

Whew! To było trudne! Mam nadzieję że to pomoże!

+1

To była niesamowita odpowiedź templatetypedef .. nic dziwnego, że masz tę nazwę użytkownika :) – cgcoder

3

The PropObject::* to wskaźnik do członka (w tym przypadku element danych). Jest to w zasadzie wskaźnik do (niestatycznego) elementu (które są w przypadku twojego kodu Student::age i Student::grade).

Aby z niego skorzystać, należy podać obiekt, którego będzie używać funkcja this. Dokonuje się tego za pomocą operatora .* lub ->*; w tym przypadku linia PropType& t = po.*Prop; obsługuje to, gdzie po jest używany jako obiekt dla członków.

1

To jest składnia dla przekazania wskaźnika do elementu jako argumentu. Szczególnie w tym przypadku jest tak, że można przekazywać elementy age i grade. Podczas gdy argumenty szablonu określają klasę Student, jest ona członkiem i że właściwości elementu są int.

Jeśli dodano nazwę ciąg do studenta deklaracja PropReader może wyglądać następująco:

PropReader<Student, std::string, &Student::name> r3; 
0

ale ten szczególny PropType PropObject :: * Prop w szablonie deklaracja mi przeszkadza. Co to znaczy?

PropType PropObject::* Prop 

ten określa wskaźnik, który wskazując zmienną do klasy, w tym konkretnym przypadku, klasa jest PropObject i typ zmiennej jest PropType.

4

deklaracje w C lub C++ są często najlepiej czytać od prawej do lewej:

PropType PropObject::* Prop 
         ~~~~ The Prop template parameter 
        ~~~  is a pointer to a member 
     ~~~~~~~~~~   of a PropObject 
~~~~~~~~      where that member has type PropType 

Można to zobaczyć w akcji w dawałaby:

PropReader<Student, int, &Student::age> r; 

Oto trzeci parametr szablonu jest wskaźnikiem do klasy Student, która ma typ int.