is cast a pointer points-to-class to it's first member illegal?

谁说胖子不能爱 提交于 2020-01-11 10:17:32

问题


I see some strange code in our project like follows.I test it and get the right answer.But I think it is illegal,Can anyone explain this to me?

class Member
{
public:
    Member():
        a(0),b(1)
    {}

    int a;
    int b;
};

// contains `Member` as its first member
class Container
{
public:
    Container():
        c(0),d(0)
    {}

    Member getMemb(){return fooObject;}

    Member fooObject;
    int c;
    int d;
};

and how we use it:

int main()
{
    auto ctain = new Container;
    auto meb = (Member *)ctain; // here! I think this is illegal

    cout << "a is " <<  meb->a << ", b is" << meb->b << endl;

    return 0;
}

but I get the right answer, a is 0 and b is 1.Is this just a coincidence?I also noted that if fooObject is not the first member, I will get a wrong answser.


回答1:


The snippet is legal. The C style cast (Member*) here is effectively a reinterpret_cast. From [basic.compound]

Two objects a and b are pointer-interconvertible if:

  • they are the same object, or

  • one is a union object and the other is a non-static data member of that object, or

  • one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no non-static data members, the first base class subobject of that object, or [...]

If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a reinterpret_­cast.

Special care should be taken to make sure it is indeed a standard layout type, possibly with a static_assert(std::is_standard_layout_v<Container>)

On the other hand, you could sidestep this entire fiasco if you just wrote auto meb = &ctain.fooObject;




回答2:


It's not exactly coincidence, it happens that your fooObject is the first member of your Container class, so the beginning of it will rest at the same starting address as the Container object. If you do:

size_t s = offsetof(Container, Container::fooObject);

It will tell that your fooObject offset will be 0, which start where your Container object start in terms of memory, so when you cast to a Member pointer it's pointing to the correct address. But for instance in other cases you would be in big trouble for sure:

class Container
{
public:
    Container() : c(0),d(0) {}

    Member getMemb(){return fooObject;}

    int c; // fooObject isn't first member
    Member fooObject;
    int d;
};

Or was a virtual class, because virtual classes store an pointer for lookup into a table.

class Container
    {
    public:
        Container() : c(0),d(0) {}
        virtual ~Container() {} // Container is virtual, and has a vtable pointer
                                // Meaning fooObject's offset into this class
                                // most likely isn't 0
        Member getMemb(){return fooObject;}

        Member fooObject;
        int c;
        int d;
    };

Someone else will have to tell you whether this cast is legal even in your example, because I'm not sure.




回答3:


C++ Standard in part 12.2 Class members:

If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member. Otherwise, its address is the same as the address of its first base class subobject (if any).

Both your classes have standard-layout. So, observed behaviour agrees with Standard.

But the cast auto meb = (Member *)ctain; breaks strict aliasing rule.



来源:https://stackoverflow.com/questions/49333703/is-cast-a-pointer-points-to-class-to-its-first-member-illegal

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!