mad*_*ada 7 c++ pointer-to-member implicit-conversion
我过去总是将代码复制并粘贴到cppinsights中,以查看代码中的哪些位置由编译器隐式执行对话。但该站点上未显示指向成员的指针标准转换。
当看到这个问题的答案时,我变得更加困惑:向下转型指向成员的指针会导致未定义的行为。我认为这个问题本身是错误的,因为据我所知,根据[conv.mem]/2可以安全地向下转换指向成员的指针;但除非应用[expr.static.cast]/12 ,否则无法安全地向上转换。这个问题有以下代码(已编辑):
struct B { int a; };
struct D1 : B {};
struct D2 : B { int b; };
void f()
{
int D1::*ptr = &B::a; // #1
int D1::*ptr = &D2::a; // #2
int B::*ptr = &D1::a; // #3
int D1::*ptr = &D2::b; // #4
}
Run Code Online (Sandbox Code Playgroud)
我的困惑只是因为我不知道到底ptr指向什么?到封闭对象本身的成员?或者封闭对象的子对象的成员?
因此,在在这里发布任何问题之前,我求助于一位 C++ 程序员(实际上是我退休的老师)来帮助我并解释上述隐式对话(如果有的话)。简而言之,这就是我得到的:
#1-这里,初始化器是指定指向成员的指针的纯右值B::a。并且由于D1具有B未命名的子对象,ptr因此可以指向a该子对象的成员;事实上,ptr指向D1::B::a,其中是的子对象B::a的成员。结果指针指向子对象的同一个成员(不是),而实际上是; 这是因为即使有一个名为 的实际非继承成员,生成的指针仍然引用该子对象的同一成员。BD1ptrBD1D1::B::aD1a
#2-这里,初始化器是指定指向成员的指针的纯右值D2::a。并且因为D2(like D1) 有B未命名的子对象,ptr所以可以指向a该子对象的成员;事实上,ptr指向D1::B::a,其中是的子对象B::a的成员。生成的指针引用子对象的同一成员(不是),这实际上是因为即使有一个名为 的实际非继承成员,生成的指针仍然引用该子对象的同一成员。BD1ptrBD1D1::B::aD1a
#3-这里,初始化器是指定指向成员的指针的纯右值D1::a。并且由于D1具有B未命名的子对象,ptr因此可以指向a该子对象的成员;事实上,ptr指向D1::B::a,其中是的子对象B::a的成员。结果指针指向子对象的同一个成员(不是),而实际上是; 这是因为即使有一个名为 的实际非继承成员,生成的指针仍然引用该子对象的同一成员。BD1ptrBD1D1::B::aD1a
#4-这里,初始化器是指定指向成员的指针的纯右值D2::b。并且由于D2没有D1子对象可以ptr指针指向其成员,因此程序需要这种转换,即使使用也是D1::a格式错误的。static_cast
其实我觉得这个解释很合逻辑,也让我满意,但是我认为它有一些错误,并不是100%正确,特别是第3段。
我的问题:
如果段落有错误,请随时更正,如果可能的话,请用更正的段落作答。谢谢,抱歉耽搁了很长时间。
我建议将指向成员的指针视为偏移量,而不是真正的指针。
因此,指向成员的指针告诉您使用什么偏移量来获取对象内的特定成员,但要取消引用它,您必须将指向成员的指针与适当类型的对象的基地址组合起来。
这意味着指向成员的指针本身并不真正指向任何东西。它(至少通常)指定可应用于实际对象的地址以获取该对象的指定成员的调整/偏移。
前三个问题的大部分实际上可以归结为如何完成名称查找。当您给出成员的名称时,编译器会查找该名称,并找出该名称引用的对象的哪一部分。指向成员的指针保存了到达该对象的实例的该成员或从该对象派生的任何对象所需的任何偏移量(在一种情况下这会变得棘手(我将在下面介绍,但您没有询问)。
因此,在前三种情况下,在 B、D1 或 D2 中的任何一个中,只有一个名为 的成员a。因此,当查找名称时a,它将引用a基类的成员,无论您如何指定它,并且指向成员的指针将保留相同的偏移量。
您的第四个案例根本不应该按照现在的情况进行编译。使用静态强制转换,您可以使其编译,但使用它的唯一方法是将其强制转换回 a D2::*(然后将其应用于类型的对象D2或从其派生的对象)。
上面我提到了一个你没有提到的有趣案例。这就是涉及多重继承的时候。在这种情况下,当您引用基类对象的成员时,编译器可能需要应用两个不同的偏移量。
struct B1 { int a; };
struct B2 { int b; };
struct D1 : public B1, public B2 {};
int B1::*ptr1 = &B1::a;
int B2::*ptr2 = &B2::b;
D1 d;
d.*ptr1 = 1;
d.*ptr2 = 2;
Run Code Online (Sandbox Code Playgroud)
在这种情况下,ptr1和分别包含和在各自类中ptr2的偏移量(通常在两种情况下均为 0)。ab
D1但是,当我们将 then 应用于(同时具有基类B1和基类)的对象时,B2编译器必须首先应用一个偏移量来获取基类子对象,然后将第二个指针应用于成员偏移量以获取该成员基础子对象。
| 归档时间: |
|
| 查看次数: |
169 次 |
| 最近记录: |