What is an `int foo::*bar::*`?
Here's a "valid" way of initializing such a monstrosity:
struct bar;
struct foo
{
int y;
int bar::* whatever;
};
struct bar
{
foo aFoo;
};
int bar::* foo::* ptr = &foo::whatever;
As we can see, ptr
is a pointer to a member of foo
(foo::*
, reading right to left), where that member is itself a pointer to a member of bar
(bar::*
), where that member is an int.
How would I use an int bar::* foo::*
You wouldn't, hopefully! But if you are under duress, try this!
struct bar
{
foo aFoo;
int really;
};
int bar::* foo::* ptr = &foo::whatever;
foo fleh;
fleh.whatever = &bar::really;
bar blah;
blah.*(fleh.*ptr) = 42;
std::cout << blah.really << std::endl;
That would be a pointer to a data member that is itself a pointer to a data member (an int
member of bar
).
Don't ask me what it is actually useful for - my head is spinning a little :)
EDIT: Here's a full example of it in action:
#include <iostream>
struct bar {
int i;
};
struct foo {
int bar::* p;
};
int main()
{
bar b;
b.i = 42;
foo f;
f.p = &bar::i;
int bar::*foo::*ptr = &foo::p;
std::cout << (b.*(f.*ptr));
}
Output is, of course, 42.
It can get even more fun - here's some pointers to member functions that return pointers to member functions:
#include <iostream>
struct bar {
int f_bar(int i) { return i; };
};
struct foo {
int(bar::*f_foo())(int)
{
return &bar::f_bar;
}
};
int main()
{
int(bar::*((foo::*ptr)()))(int) = &foo::f_foo;
bar b;
foo f;
std::cout << (b.*((f.*ptr)()))(42);
}
Let's parse the declaration int bar::*foo::*ptr;
.
§8.3.3 [dcl.mptr]/p1:
In a declaration
T D
whereD
has the formnested-name-specifier * attribute-specifier-seq_opt cv-qualifier-seq_opt D1
and the nested-name-specifier denotes a class, and the type of the identifier in the declaration
T D1
is “derived-declarator-type-listT
”, then the type of the identifier ofD
is “derived-declarator-type-list cv-qualifier-seq pointer to member of class nested-name-specifier of typeT
”.
Step 1: This is a declaration of the above form where
T
= int, nested-name-specifier =bar::
, andD1 = foo::* ptr
. We first look at the declarationT D1
, orint foo::* ptr
.Step 2: We apply the same rule again.
int foo::* ptr
is a declaration of the above form whereT
= int, nested-name-specifier =foo::
, andD1
=ptr
. Obviously the type of the identifier inint ptr
is "int
", so the type of the identifierptr
in the declarationint foo::* ptr
is "pointer to member of classfoo
of typeint
".Step 3. We go back to the original declaration; the type of the identifier in
T D1
(int foo::* ptr
) is "pointer to member of classfoo
of typeint
" per step 2, so the derived-declarator-type-list is "pointer to member of classfoo
of type". Substitution tells us that this declaration declaresptr
to be "pointer to member of classfoo
of type pointer to member of classbar
of typeint
".
Hopefully you will never need to use such a monstrosity.
In case anyone is wondering, you can't create a pointer-to-member which nests multiple layers deep. The reason for this is that all pointer-to-members are actually way more complicated that what they look at a first glance; they are not simply containing a particular offset for that specific member.
Using a simple offset does not work due to virtual inheritance and the likes; basically it can happen that, even within a single type, the offsets of a particular field vary between instances, and thus pointer-to-member resolution needs to be done at runtime. Mostly this is due to the fact that the standard does not specify how the internal layout for non-POD types might work, so there's no way to make it work statically.
If this is the case, doing two-level deep resolution cannot be done with a normal pointer-to-member, but would need the compiler to generate a pointer such that it contains double the information of a one-deep pointer-to-member.
I imagine that since pointers-to-member are not that common, there is no need to actually create a syntax to allow for setting multiple-layer deep members, when you can still use multiple pointers to achieve the same result.