16

我是一名新手 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++ 专家的解释。我想了解它,以便更好地使用它。虽然它看起来非常有用。

4

5 回答 5

19

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,它指向 aPropObject类型的字段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转换为 type 的引用PropObject。我猜这个想法是Object许多不同对象的一些基类。函数的参数只是一个普通的Object, 并且此转换尝试将其转换为适当类型的东西(回想一下,PropObject模板参数说明对象的类型是什么)。因为这使用static_cast,如果未定义转换(例如,您尝试实例化模板超过intor vector<string>),代码将无法编译。否则,代码相信强制转换是安全的,然后获取PropObject对参数所指的类型的引用。

最后,最后一行是

PropType& t = po.*Prop;

这使用了我之前提到的指向类成员的指针解引用语法来表示“选择Prop(模板参数)指向的字段,然后存储对它的引用,命名为t.

所以,简而言之,模板

  1. 询问您某些对象的类型。
  2. 询问您该对象中某个字段的类型。
  3. 要求您提供指向该对象中字段的指针。
  4. 提供print给定对象尝试打印该字段的函数。

哇!那很棘手!希望这可以帮助!

于 2011-07-22T00:48:27.263 回答
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该类成员的指针,该成员具有 type int

于 2011-07-22T00:44:24.250 回答
3

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

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

于 2011-07-22T00:37:44.390 回答
1

这就是将指向成员的指针作为参数传递的语法。特别是在这种情况下,可以传入成员。虽然模板参数指定了它所属的类,age并且成员属性是.gradeStudentint

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

PropReader<Student, std::string, &Student::name> r3;
于 2011-07-22T00:40:01.957 回答
0

但是模板声明中的这个特殊的 PropType PropObject::* Prop 困扰着我。这是什么意思?

PropType PropObject::* Prop

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

于 2011-07-22T00:41:13.333 回答