Сумасшедший шаблон C ++ - шаблон для доступа к отдельным атрибутам класса - PullRequest
15 голосов
/ 22 июля 2011

Я начинающий программист C ++, но мне показалось, что я достаточно знаю о C ++ до сегодняшнего дня, пока не наткнулся на такой код на работе и не смог понять, как он на самом деле работает.

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

Мне кажется, я поняла на высоком уровне. Но этот конкретный PropType PropObject::* Prop в объявлении шаблона беспокоит меня. Что это значит? Я ищу объяснение от экспертов C ++. Я хотел бы понять это, чтобы я мог использовать его лучше. Это выглядит очень полезным, хотя.

Ответы [ 5 ]

18 голосов
/ 22 июля 2011

Шаблоны C ++ хорошо известны тем, что они принимают типы в качестве аргументов, но они также могут параметризоваться и для других типов данных. Например, вы можете шаблонизировать класс по целому числу, как показано здесь:

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

public:
    /* ... */
};

Шаблоны также можно параметризировать по указателям, если указатель соответствует определенным критериям (например, он должен вычислять адрес чего-либо, что может быть определено во время компиляции). Например, это совершенно законно:

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

В вашем коде шаблон параметризован через указатель на член класса . Указатель на член класса похож на указатель тем, что косвенно ссылается на некоторый объект. Однако вместо указания на объект он указывает на поле в классе. Идея состоит в том, что вы можете разыменовать указатель на член класса относительно некоторого объекта, чтобы выбрать это поле из класса. Вот простой пример указателей на член класса:

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.
}

Обратите внимание, что синтаксис объявления указателя на член класса -

Type ContainingClass::* pointerName;

Таким образом, в приведенном выше коде int MyStruct::* ptr означает «указатель на int внутри MyStruct класса.

В опубликованном вами коде объявление шаблона выглядит следующим образом:

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

Посмотрим, что это значит. Первые два объекта аргумента шаблона, свойство которых будет считано, и PropType, тип этого свойства. "Последний аргумент шаблона - указатель на член класса с именем Prop, который указывает внутри PropObject в поле типа PropType. Например, вы можете создать экземпляр этого шаблона с помощью MyStruct следующим образом:

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

Теперь посмотрим, что делает остальная часть кода. Тело этого шаблона класса перепечатано здесь:

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

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

Некоторые из них можно прочитать довольно легко. Параметром этой функции является ссылка на Object с именем o, а в последней строке печатается какое-то поле. Эти две строки хитры:

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

Эта первая строка представляет собой тип-трансляцию, в которой говорится "попытаться привести аргумент o к ссылке типа PropObject. Идея, я предполагаю, состоит в том, что Object - это некоторый базовый класс множества различные объекты. Параметром для функции является просто Object, и это приведение пытается преобразовать его в нечто подходящего типа (напомним, что PropObject является аргументом шаблона, указывающим, какой тип объекта). при этом используется static_cast, если преобразование не определено (например, вы пытались создать экземпляр шаблона через int или vector<string>), код не будет компилироваться, в противном случае код верит, что приведение безопасно , затем получает ссылку типа PropObject на то, к чему относится параметр.

Наконец, последняя строка -

PropType& t = po.*Prop;

При этом используется синтаксис разыменования указатель на член класса, о котором я упоминал ранее, чтобы сказать «выберите поле, на которое указывает Prop (аргумент шаблона), затем сохраните ссылку на него с именем t.

Итак, вкратце, шаблон

  1. Запрашивает тип какого-либо объекта.
  2. Запрашивает тип поля в этом объекте.
  3. Запрашивает указатель на поле в этом объекте.
  4. Предоставляет функцию print, которая для данного объекта пытается распечатать это поле.

Уф! Это было сложно! Надеюсь, это поможет!

4 голосов
/ 22 июля 2011

Объявления в C или C ++ часто лучше всего читать справа налево:

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

Это можно увидеть в действии в экземплярах:

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

Здесь третий параметр шаблона представляет собой указатель на член класса Student, который имеет тип int.

3 голосов
/ 22 июля 2011

PropObject::* - указатель на элемент (в данном случае, элемент данных). В основном это указатель на (нестатический) член (в случае вашего кода Student::age и Student::grade).

Чтобы использовать его, вы должны указать объект this, который будет использовать функция. Это делается с помощью оператора .* или ->*; в этом случае строка PropType& t = po.*Prop; обрабатывает это, где po используется в качестве объекта this для членов.

1 голос
/ 22 июля 2011

Это синтаксис для передачи указателя на член в качестве аргумента.В частности, в этом случае это так, что члены age и grade могут быть переданы. В то время как аргументы шаблона указывают класс Student, членом которого он является, и свойства этого элемента int.

* 1006.* Если вы добавили строковое имя к студенту, объявление PropReader может выглядеть так:
PropReader<Student, std::string, &Student::name> r3;
0 голосов
/ 22 июля 2011

Но этот конкретный PropType PropObject :: * Prop в шаблоне Декларация беспокоит меня. Что это значит?

PropType PropObject::* Prop

Определяет указатель, указывающий переменную-член класса, в данном конкретном случае классом является PropObject, а тип переменной - PropType.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...