Question

What I am needing can be done by storing this pointer of enclosing class into nested class for example this way:

class CEnclosing {
public:
    class CNested : public CSomeGeneric {
    public: 
        CNested(CEnclosing* e) : m_e(e) {}
        virtual void operator=(int i) { m_e->SomeMethod(i); }
        CEnclosing* m_e;
    };

    CNested nested;

    CEnclosing() : nested(this) {}

    virtual void SomeMethod(int i);
};


int main() 
{
    CEnclosing e;
    e.nested = 123;
    return 0;
}

This works well, but requires sizeof(void*) bytes of memory more for each nested member class. Exist effective and portable way to do this without need to store pointer to instance of CEnclosing in m_e?

Was it helpful?

Solution

As stated previously, C++ does not provide any way to do this. A nested class has no special way to find its enclosing class. The solution you already have is the recommended way.

If you have an advanced scenario, and if you are prepared to maintain non-portable code, and if the cost of storing an additional pointer is important enough to use a risky solution, then there is a way based on the C++ object model. With a number of provisos I won't go into, you can rely on the enclosing and nested classes being laid out in memory in a predictable order, and there being a fixed offset between the start of the enclosing and nested classes.

The code is something like:

   CEnclosing e;
   int offset = (char*)&e.nested - (char*)&e;
   //... inside nested class
   CEnclosing* pencl = (CEnclosing*)((char*)this - offset);

OTOH it's equally possible that the offsetof macro may just do it for you, but I haven't tried it.

If you really want to do this, read about trivially copyable and standard layout in the standard.

OTHER TIPS

I believe the following could be portable; though it is not fool-proof. Specifically, it will not work across virtual inheritance.

Also, I would like to point that it is not safe, in that it will happily compile even if the member you pass does not correspond to the one you compute the offset with:

#include <iostream>

template <typename C, typename T>
std::ptrdiff_t offsetof_impl(T C::* ptr) {
    C c; // only works for default constructible classes
    T* t = &(c.*ptr);
    return reinterpret_cast<char*>(&c) - reinterpret_cast<char*>(t);
}

template <typename C, typename T, T C::* Ptr>
std::ptrdiff_t offsetof() {
    static std::ptrdiff_t const Offset = offsetof_impl(Ptr);
    return Offset;
}

template <typename C, typename T, T C::* Ptr>
C& get_enclosing(T& t) {
    return *reinterpret_cast<C*>(reinterpret_cast<char*>(&t)
         + offsetof<C, T, Ptr>());
}

// Demo
struct E { int i; int j; };

int main() {
    E e = { 3, 4 };

    //
    // BEWARE: get_enclosing<E, int, &E::j>(e.i); compiles ERRONEOUSLY too.
    //                                   ^ != ^
    //
    E& ref = get_enclosing<E, int, &E::j>(e.j);

    std::cout << (void const*)&e << " " << (void const*)&ref << "\n";
    return 0;
}

Still, it does run on this simplistic example, which allowed me to find 2 bugs in my initial implementation (already). Handle with caution.

The clear and simple answer to your question is no, C++11 doesn't have any special feature to handle your scenario. But there is a trick in C++ to allow you to do this:

If CEnclosing didn't have a virtual function, a pointer to nested would have the same value as a pointer to the containing instance. That is:

(void*)&e == (void*)&e.nested

This is because the variable nested is the first in the class CEnclosing.

However, since you have a virtual function in CEnclosing class, then all you need to do is subtract the vtable size from &e.nested and you should have a pointer to e. Don't forget to cast correctly, though!


EDIT: As Stephane Rolland said, this is a dangerous solution and, honestly, I wouldn't use it, but this is the only way (or trick) I could think of to access the enclosing class from a nested class. Personally, I would probably try to redesign the relation between these two classes if I really want to optimise memory usage up to the level you mentioned.

How about using multiple inheritance like this:

class CNested {
public: 
    virtual void operator=(int i) { SomeMethod(i); }
    virtual void SomeMethod(int i) = 0;
};

class CEnclosing: public CSomeGeneric, public CNested {
    int nEncMember;
public:
    CNested& nested;
    CEnclosing() : nested(*this), nEncMember(456) {}
    virtual void SomeMethod(int i) { std:cout << i + nEncMember; }
};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top