Does this pointer adjustment occur for non-polymorphic inheritance?

隐身守侯 提交于 2020-01-02 10:01:11

问题


Does non-polymorphic inheritance require this pointer adjustment? In all the cases I've seen this pointer adjustment discussed the examples used involved polymorphic inheritance via keyword virtual.

It's not clear to me if non-polymorphic inheritance would require this pointer adjustment.

An extremely simple example would be:

struct Base1 {
    void b1() {}
};

struct Base2 {
    void b2() {}
};

struct Derived : public Base1, Base2 {
    void derived() {}
};

Would the following function call require this pointer adjustment?

Derived d;
d.b2();

In this case the this pointer adjustment would clearly be superfluous since no data members are accessed. On the other hand, if the inherited functions accessed data members then this pointer adjustment might be a good idea. On the other other hand, if the member functions are not inlined it seems like this pointer adjustment is necessary no matter what.

I realize this is an implementation detail and not part of the C++ standard but this is a question about how real compilers behave. I don't know if this is a case like vtables where all compilers follow the same general strategy or if I've asked a very compiler dependent question. If it is very compiler dependent, then that in itself would be a sufficient answer or if you'd prefer, you can focus on either gcc or clang.


回答1:


Layout of objects is not specified by the language. From the C++ Draft Standard N3337:

10 Derived Classes

5 The order in which the base class subobjects are allocated in the most derived object (1.8) is unspecified. [ Note: a derived class and its base class subobjects can be represented by a directed acyclic graph (DAG) where an arrow means “directly derived from.” A DAG of subobjects is often referred to as a “subobject lattice.”

6 The arrows need not have a physical representation in memory. —end note ]

Coming to your question:

Would the following function call require this pointer adjustment?

It depends on how the object layout is created by the compiler. It may or may not.

In your case, since there are no member data in the classes, there are no virtual member functions, and you are using the member function of the first base class, you probably won't see any pointer adjustments. However, if you add member data, and use a member function of the second base class, you are most likely going to see pointer adjustments.

Here's some example code and the output from running the code:

#include <iostream>

struct Base1 {
   void b1()
   {
      std::cout << (void*)this << std::endl;
   }
   int x;
};

struct Base2 {
   void b2()
   {
      std::cout << (void*)this << std::endl;
   }
   int y;
};

struct Derived : public Base1, public Base2 {
   void derived() {}
};

int main()
{
   Derived d;
   d.b1();
   d.b2();
   return 0;
}

Output:

0x28ac28
0x28ac2c



回答2:


This is not just compiler-specific but also optimization-level-specific. As a rule of thumb, all this pointers are adjusted, only sometimes it is by 0 as would be your example in many compilers (but definitely not all — IIRC, MSVC is a notable exception). If the function is inlined and does not access this, then the adjustment may be optimized out altogether.




回答3:


Using R Sahu's method for testing this, it looks like the answer for gcc, clang, and icc is yes, this pointer adjustment occurs, unless the base class is the primary base class or an empty base class.

The test code:

#include <iostream>

namespace {
struct Base1
{
    void b1()
    {
        std::cout << "b1() " << (void*)this << std::endl;
    }

    int x;
};

struct Base2
{
    void b2()
    {
        std::cout << "b2() " << (void*)this << std::endl;
    }

    int x;
};

struct EmptyBase
{
    void eb()
    {
        std::cout << "eb(): " << (void*)this << std::endl;
    }
};

struct Derived : private Base1, Base2, EmptyBase
{
    void derived()
    {
        b1();
        b2();
        eb();
        std::cout << "derived(): " << (void*)this << std::endl;
    }
};
}

int main()
{
    Derived d;
    d.derived();
}

An anonymous namespace is used to give the base classes internal linkage. An intelligent compiler could determine that the only use of the base classes is in this translation unit and this pointer adjustment is unnecessary. Private inheritance is used for good measure but I don't think it has real significance.

Example g++ 4.9.2 output:

b1() 0x7fff5c5337d0
b2() 0x7fff5c5337d4
eb(): 0x7fff5c5337d0
derived(): 0x7fff5c5337d0

Example clang 3.5.0 output

b1() 0x7fff43fc07e0
b2() 0x7fff43fc07e4
eb(): 0x7fff43fc07e0
derived(): 0x7fff43fc07e0

Example icc 15.0.0.077 output:

b1() 0x7fff513e76d8
b2() 0x7fff513e76dc
eb(): 0x7fff513e76d8
derived(): 0x7fff513e76d8

All three compilers adjust the this pointer for b2(). If they don't elide the this pointer adjustment in this easy case then they very likely won't ever elide this pointer adjustment. The primary base class and empty base classes are exceptions.

As far as I know, an intelligent standards conforming compiler could elide the this pointer adjustment for b2() but it's simply an optimization that they don't do.



来源:https://stackoverflow.com/questions/28372365/does-this-pointer-adjustment-occur-for-non-polymorphic-inheritance

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