Crazy C ++模板 - 用于访问类的各个属性的模板

时间:2011-07-22 00:32:39

标签: c++ templates pointer-to-member

我是一名新手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 个答案:

答案 0 :(得分:18)

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,如果未定义转换(例如,您尝试在intvector<string>上实例化模板),代码将无法编译。否则,代码信任转换是安全的,然后获取类型PropObject的引用到参数引用的内容。

最后,最后一行是

PropType& t = po.*Prop;

这使用前面提到的指向类成员的解引用语法来说“选择Prop指向的字段(模板参数),然后存储名为t的引用

简而言之,就是模板

  1. 询问您某个对象的类型。
  2. 询问您该对象中某些字段的类型。
  3. 询问您指向该对象中字段的指针。
  4. 提供print函数,给定对象尝试打印该字段。
  5. 呼!那太棘手了!希望这有帮助!

答案 1 :(得分:4)

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

答案 2 :(得分:3)

PropObject::*是指向成员的指针(在本例中为数据成员)。它基本上是指向(非静态)成员的指针(在代码的情况下为Student::ageStudent::grade)。

要使用它,您必须指定该函数将使用的this对象。这是通过使用.*->*运算符完成的;在这种情况下,PropType& t = po.*Prop;行处理,其中po用作成员的this对象。

答案 3 :(得分:1)

这是将指针作为参数传递给成员的语法。特别是在这种情况下,可以传入agegrade成员。虽然模板args指定Student类,但它是成员,成员属性为{{1 }}

如果您向学生添加了字符串名称,则PropReader声明可能如下所示:

int

答案 4 :(得分:0)

  

但是这个特定的PropType PropObject :: * Prop在模板中   宣言困扰我。这是什么意思?

PropType PropObject::* Prop

这定义了指向类的成员变量的指针,在这种特殊情况下,该类是PropObject,变量类型是PropType。