3

In C++, it is small sample code that using std::enable_shared_from_this and inheritance. The p in this code, call a member function fb and fa. The p is the same object, but called fa and fb take a different this address.

Why take a different address?

code:

#include <memory>
#include <iostream>

template<class T> using S = std::enable_shared_from_this<T>;

struct A: S<A>    { auto fa() const -> void { std::cerr << this << "\n";} };
struct B: S<B>, A { auto fb() const -> void { std::cerr << this << "\n";} };

auto main() -> int
{
    auto p = std::make_shared<B>();

    p -> fa();
    p -> fb();

    std::cerr << p << "\n";
}

result: (tested compilers are clang++-3.2, g++-4.8 and em++-1.12 on Linux Mint 16 KDE and clang++-3.4 in wandbox as a online compiling platform.)

0x8376a8
0x837698
0x837698

wandbox: http://melpon.org/wandbox/permlink/rmcCrR0rqLd7tKfV

lrineau
  • 6,036
  • 3
  • 34
  • 47
Usagi Ito
  • 483
  • 7
  • 16
  • 2
    This doesn't even compile with libstdc++, deriving twice from a `enable_shared_from_this` specialization leads to an ambiguous call. – dyp Apr 10 '14 at 12:58
  • Revert the order of the multiple inheritance and try again. [Surprise!](http://melpon.org/wandbox/permlink/be4OM6mmpLJNl4qr) – dyp Apr 10 '14 at 13:00
  • possible duplicate of [Can 'this' pointer be different than the object's pointer?](http://stackoverflow.com/questions/18368594/can-this-pointer-be-different-than-the-objects-pointer) – n. m. could be an AI Apr 10 '14 at 13:02

1 Answers1

5

This has nothing to do with std::enable_shared_from_this; it's the general scenario of multiple inheritance. Inside a member function of class X, the type of this is X*, i.e. it points to the X subobject of the complete object. So when calling A::fa() on a B object, this refers to the A subobject of B. That can be offset from the object's initial address, based on the compiler's chosen memory layout.

In other words, an object B contains both a base class object A and a base class object S<B>. Since you listed S<B> first, the compiler chose to lay out S<B> first in B's layout, and A after it. So the memory layout looks something like this:

+-------+-----+---------------------------+
| S<B>  | A   | B's non-inherited members |
+-------+-----+---------------------------+
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455