16

I am a novice C++ programmer, but I thought I know enough about C++ until today when I came across code like this at work and failed to understand how it actually works.

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

I think I kind of understood at a high level. But this particular PropType PropObject::* Prop in the template declaration bothers me. What does it mean? I am looking for an explanation from C++ experts. I would like to understand it, so that I can use it better. It looks very useful though.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
cgcoder
  • 426
  • 4
  • 11

5 Answers5

19

C++ templates are well-known for taking types as arguments, but they can also be parameterized over other types of data as well. For example, you could templatize a class over an integer, as shown here:

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

public:
    /* ... */
};

Templates can also be parameterized over pointers, as long as the pointer meets certain criteria (for example, it has to evaluate to an address of something that can be determined at compile-time). For example, this is perfectly legal:

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

In your code, the template is parameterized over a pointer-to-class-member. A pointer-to-class-member is similar to a pointer in that it indirectly refers to some object. However, instead of pointing to an object, instead it points to a field in a class. The idea is that you can dereference a pointer-to-class-member relative to some object to select that field out of the class. Here's a simple example of pointers-to-class-member:

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

Notice that the syntax for declaring a pointer-to-class-member is

Type ContainingClass::* pointerName;

So in the above code, int MyStruct::* ptr means "a pointer to an int inside of a MyStruct class.

In the code that you've posted, the template declaration reads like this:

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

Let's see what this means. The first two template argument object whose property is going to be read, and PropType, the type of that property." The final argument to the template is a pointer-to-class-member named Prop that points inside a PropObject at a field of type PropType. For example, you could instantiate this template with MyStruct like this:

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

Now, let's see what the rest of the code does. The body of this class template is reprinted here:

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

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

Some of this can be read pretty easily. The parameter to this function is a reference to an Object named o, and the last line prints out some field. These two lines are tricky:

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

This first line is a typecast that says "try to cast the argument o to a reference of type PropObject. The idea, I'm guessing, is that Object is some base class of a lot of different objects. The parameter to the function is just a plain Object, and this cast tries to convert it to something of the appropriate type (recall that PropObject is the template argument saying what the type of the object is). Because this uses static_cast, if the conversion isn't defined (for example, you tried to instantiate the template over int or vector<string>), the code won't compile. Otherwise, the code trusts that the cast is safe, then gets a reference of type PropObject to what the parameter refers to.

Finally, the last line is

PropType& t = po.*Prop;

This uses the pointer-to-class-member dereference syntax I mentioned earlier to say "select the field pointed at by Prop (the template argument), then store a reference to it named t.

So, in short, the template

  1. Asks you for the type of some object.
  2. Asks you for the type of some field in that object.
  3. Asks you for a pointer to the field in that object.
  4. Provides a print function that given an object tries to print out that field.

Whew! That was tricky! Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
4

Declarations in C or C++ are often best read from right to left:

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

This can be seen in action in the instantiations:

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

Here the third template parameter is a pointer to a member of the Student class, that has type int.

sth
  • 222,467
  • 53
  • 283
  • 367
3

The PropObject::* is a pointer-to-member (data member, in this case). It's basically a pointer to a (non-static) member (which are Student::age and Student::grade in the case of your code).

To use it, you have to specify the this object that the function would use. That's done by using either the .* or ->* operator; in this case, the PropType& t = po.*Prop; line handles that, where po is used as the this object for the members.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
1

That's the syntax for passing a pointer to a member as an argument. Specifically in this case it's so that age and grade members can be passed in. While the template args are specifying the Student class it's a member of and that the member properties are int.

If you added a string name to student the PropReader declaration might look like this:

PropReader<Student, std::string, &Student::name> r3;
AJG85
  • 15,849
  • 13
  • 42
  • 50
0

But this particular PropType PropObject::* Prop in template declaration bothers me. What does it mean?

PropType PropObject::* Prop

This defines pointer that pointing a member variable of a class, in this particular case, the class is PropObject and the variable type is PropType.

RoundPi
  • 5,819
  • 7
  • 49
  • 75