【问题标题】:(Why) can we assign non-static class members to static variables in initialization?(为什么)我们可以在初始化时将非静态类成员分配给静态变量吗?
【发布时间】:2020-09-21 05:10:11
【问题描述】:

在更大的代码库中,我遇到过这样的代码 (see on godbolt):

struct Foo {};

struct Base {
    Foo* a;
};

struct Bar : public Base {
    static Foo* Bar::*mybar[];
};

Foo* Bar::Bar::*mybar[] = {
    &Base::a
};

说实话,我很困惑。这看起来像是在使用Base 的非静态成员变量初始化Bar 中的Foo 指针的静态数组。没有对象怎么可能?

(免责声明:这是在实际工作的生产代码中发现的 - 希望不依赖于 UB?)

另外,如果我删除像here 这样的限定名称查找有什么问题吗?我想重构代码并使其更具可读性。所有这些Bar::s 似乎都是多余的,但由于我对代码感觉不太舒服,我宁愿先了解其中的含义。

【问题讨论】:

  • 你把&Base::a成员指针和Base b; &b.a;对象指针混淆了吗?
  • 生产代码真的是Foo *Bar::Bar::*mybar,还是Foo *Bar::* Bar::mybar
  • @1201ProgramAlarm 感谢您的注意:确实这是一个错字 - 我已经更新了我的问题,包括上帝螺栓链接。

标签: c++ class c++11 inheritance static


【解决方案1】:

与普通指针不同,类成员指针可以看作是类的偏移量,它们告诉您它们指向对象的哪个成员。所以在你的代码中,mybar 是一个类成员指针数组。当你这样做时

Foo *Bar::Bar::*mybar[] = {
    &Base::a
};

您使用指向Basea 成员的指针初始化数组。这实际上并不是指向a,它只是告诉编译器如果您使用对象访问它,则返回该对象的哪个成员。看起来像

Base foo; // now we have an actual object
foo.*mybar[0]; // access the `a` member of `foo` by using the "offset"

【讨论】:

    【解决方案2】:

    首先,Bar::mybarpointers to members 的数组。这些不是实际的指针。它们更像是对对象偏移的抽象。它们允许间接访问成员。给定一个Base 对象(或从它派生的对象),我们可以调用它们,如下所示

    aBar.*mybar[0] // This resolve to a Foo* inside aBar
    

    另外需要注意的是,在您的示例中,命名空间范围内的对象不是Bar::mybar 的定义。这是一个不相关的数组。正确的定义是

    Foo* Bar::* Bar::mybar[] = {
        &Base::a
    };
    

    因为你的定义是错误的,所以删除一些限定名没有效果。当你删除它时,你就剩下了

    static Foo *mybar[];
    
    Foo Bar::*mybar[] = {
        &Base::a
    };
    

    声明的类型和“定义”不匹配。但是你没有出错,因为它们实际上是不同的对象。

    在正确的定义中,每个Bar:: 限定符都是必需的,并且用于不同的目的。一个用于创建正确的类型(指向Bar 的成员的指针),另一个指定正在定义的Bar 的静态成员,并且在每个静态类成员定义中都是必需的。

    【讨论】:

    • 感谢您的注意:确实这是一个错字 - 我已经更新了我的问题,包括 godbolt 链接。但是here,如果第8行和第11行不重合,为什么Bar中没有对应的声明,第11行还要编译?
    • 我已经在回答中提到了这个问题。您的定义与Bar 中的任何声明都不对应。它是TYPE mybar[] = { ... }; 形式的命名空间范围的声明。这没有定义Bar 的成员。为此,您需要TYPE Bar::mybar ...
    • 哇,这是一个快速的回复 :-) 我想我明白你的意思,但需要更深入地挖掘。但你肯定解决了鞋夹的问题!谢谢。
    【解决方案3】:

    没有对象怎么可能?

    可以在没有对象的情况下创建指向成员变量的指针。只有在对象存在时,指针才能用于取消引用实际成员。

    更简单的例子:

    struct Foo { int m; int n};
    
    using MemberPtr = int Foo::*;
    
    MemberPtr p1 = &Foo::m;  // Instance of Foo is not needed.
    MemberPtr p2 = &Foo::n;  // Instance of Foo is not needed.
    
    *p1 = 10; //  Not allowed.
    *p2 = 20; //  Not allowed.
    
    Foo a;
    a.*p1 = 10;  // Changes a.m
    a.*p2 = 20;  // Changes a.n
    
    Foo b;
    b.*p1 = 30;  // Changes b.m
    b.*p2 = 40;  // Changes b.n
    

    请注意,您可以使用指向成员变量的相同指针来更改类的两个实例的成员值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-05-02
      • 2012-03-28
      • 2020-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多