Question

I'd like to compose member pointers. Basically I have a main class with different member. How do I create a member pointer for the main class that would point to a member of a member of that class. I hope the code below is explains what I'm trying to do:

  struct SubUnit
  {
    int   value;
  };
  struct Unit
  {
    SubUnit sub_unit;
  };

  void Test()
  {
    SubUnit Unit::* ptr1 = &Unit::sub_unit; // WORKING
    int Unit::* ptr2 = &Unit::sub_unit::value; // NOT WORKING !
  }
Was it helpful?

Solution 2

The use of sub-class is confusing here, since generally sub-class is used for inheritance, so let us talk about a data member: sub_unit is a data member of Unit.

And what you are asking for is not possible, Unit::* can only represent an offset in either Unit itself or one of its base-classes:

struct SubUnit { int value; };

struct Unit: SubUnit {};

int main() { int Unit::* p = &Unit::value; }

OTHER TIPS

It seems you have to do it in two phases:

SubUnit Unit::*pSub = &Unit::sub_unit;
int SubUnit::*pValue = &SubUnit::value;

Unit u;
int theVal = (u.*pSub).*pValue;

This seems like a duplicate of this question:
Nested data member pointer - not possible?

The crux of the accepted answer says:

A pointer-to-member can only be formed by an expression of type &qualified_id, which is not your case

The C++ standard says, in paragraph 8.3.3:
Pointer to Members:

In a declaration T D where D has the form

  nested-name-specifier * attribute-specifier-seqopt cv-qualifier-seqopt D1

and the nested-name-specifier denotes a class, and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is “derived-declarator-type-list cv qualifier- seq pointer to member of class nested-name-specifier of type T”. The optional attribute-specifier-seq (7.6.1) appertains to the pointer-to-member.

It turns out, there's a way to do what you want, that seems to be working on my current compiler (apple llvm 5.0). However, it is very implementation-dependant, and I would definitely recommend not using this solution in any kind of production code:

//added a few data members to make it non-trivial
struct SubUnit {
    double d;
    int   value;
};
struct Unit {
    bool b;
    char c;
    SubUnit sub_unit;
};

intptr_t suOffset = offsetof(Unit, sub_unit);
intptr_t intOffset = offsetof(SubUnit, value);
intptr_t totalOffset = suOffset + intOffset;

// there is no way to convert this offset directly in a
// pointer-to-member AFAIK, so we have to trick a bit
int Unit::* pui = nullptr;
auto puiAddr = &pui;
intptr_t* puiAddrAsIntPtrPtr = reinterpret_cast<intptr_t*>(puiAddr);
*puiAddrAsIntPtrPtr = totalOffset;

//pui should now "point to the right offset"
//let's test it
Unit u;
u.sub_unit.value = 123456;

int val = u .* pui;
std::cout << "val: " << val << std::endl;

A workaround can be constructed with a lambda:

    struct SubUnit
    {
        int   value;
    };
    struct Unit
    {
        SubUnit sub_unit;
    };

    void Test()
    {
        auto  composed_mem_ptr = [](Unit & unit) -> int&{ return  unit.sub_unit.value; };
        Unit  unit0{};
        composed_mem_ptr(unit0) = 7;
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top