### What does "S::" mean in the declaration "int S::* pmi = &S::mi"?

This is a two-part question.

1. I'm trying to understand how to use the pointer-to-member access operator but I don't understand the example code provided by [1].

Specifically, I don't understand what's being declared on line 17 and 18:

 ``1718`` ``````int S::* pmi = &S::mi; int (S::* pf)(int) = &S::f;``````

Line 17 looks like the initialization of a pointer `pmi` but what the heck is `int S::` part of the type? Does it mean "an int, but not just any int; an int that must be inside the class S"? I guess it could also be a "scoped pointer", as in, "an S-class member pointer that is an int". -\_( o.O)_/-

Line 18 looks like a function pointer initialization (`pf` is a pointer to a function that takes int and returns an int). But, again, what the heck is up with `S::`?

2. Does `lhr->*rhs` mean `*(*(lhs).rhs)`?

 ``12345678910111213141516171819202122232425262728`` ``````#include   struct S { S(int n): mi(n) {} mutable int mi; int f(int n) { return mi + n; } };   struct D: public S { D(int n): S(n) {} };   int main() { int S::* pmi = &S::mi; int (S::* pf)(int) = &S::f;   const S s(7); //s.*pmi = 10; // error: cannot modify through mutable std::cout << s.*pmi << '\n';   D d(7); // base pointers work with derived object D* pd = &d; std::cout << (d.*pf)(7) << ' ' << (pd->*pf)(8) << '\n'; }``````

So `(pd->*pf)(8)` is a function invocation and my hypothesis is that it is equivalent to `*(*(pd).pf)(8)`

[1] cppref: C++ Operator Precedence
https://en.cppreference.com/w/cpp/language/operator_member_access#Built-in_pointer-to-member_access_operators
Last edited on
 I don't understand what's being declared on line 17 and 18

In a sense they are not really pointers at all. Consider that even though you've initialized them, they are not pointing to anything! They need an underlying struct/class to offset from before they actually "point" to anything. So they are more like offsets than pointers.

 Does lhr->*rhs mean *(*(lhs).rhs)?

No. The analogy to a->b is:

`a->b` means `(*a).b`

`a->*b` means `(*a).*b`

 So (pd->*pf)(8) is a function invocation and my hypothesis is that it is equivalent to *(*(pd).pf)(8)

An easy hypothesis to test.

 ``123456789101112131415161718192021222324252627282930`` ``````#include struct T { int x, y; int a(int n) { return n * 2; } int b(int n) { return n * 3; } }; int main() { T t {1, 2}; int T::* p = &T::x; std::cout << t.*p << '\n'; // 1 p = &T::y; std::cout << t.*p << '\n'; // 2 int (T::* pf)(int) = &T::a; std::cout << (t.*pf)(7) << '\n'; // 14 pf = &T::b; std::cout << (t.*pf)(7) << '\n'; // 21 T* tp = &t; std::cout << tp->*p << '\n'; // 2 // Test of hypothesis // std::cout << *(*(tp).pf)(8) << '\n'; // will not compile // This works: std::cout << ((*tp).*pf)(8) << '\n'; }``````

Last edited on
DizzyDon wrote:
a->*b means (*a).*b

Thanks for the correction. Indeed, the member-access operator `.` has higher precedence than the dereference `*` operator. What I meant to type, instead of `*(*(lhs).rhs)`, was `*((*lhs).rhs)`.

The `.*` operator, as the notation/precedence rule suggests, is just member-access followed by a dereference of that member, right?

So does `(*a).*b` also mean `*((*a).b)`?

DizzyDon wrote:
In a sense they are not really pointers at all. Consider that even though you've initialized them, they are not pointing to anything! They need an underlying struct/class to offset from before they actually "point" to anything. So they are more like offsets than pointers.

That's really strange. I've never come across that concept before. Is there a name for it?

Plugging the `int T::* p` declaration into [2], I get the english equivalent of `"declare p as pointer to member of class T int"`.

[2] cdecl: C gibberish <-> English
https://cdecl.org/
Last edited on
ElusiveTau wrote:
The .* operator, as the notation/precedence rule suggests, is just member-access followed by a dereference of that member, right?

So does (*a).*b also mean *((*a).b)?

Apparently not!

 ``1234567891011121314151617`` ``````#include struct S { int *pb = nullptr; S(int *n) : pb(n) {} }; int main() { int x = 10; S s1{&x}; S *s1ptr = &s1; std::cout << ++(*(s1ptr->pb)); // Increment x by 1, print the result std::cout << ++(s1ptr->*pb); // Was hoping to do the same but got: "identifier "pb" is undefined" }``````

https://learn.microsoft.com/en-us/cpp/cpp/pointer-to-member-operators-dot-star-and-star?view=msvc-170

I see. First, pointers not only can point to types:
 ``123456789101112`` ``````struct Foo { int fooi1 = 10; int fooi2 = 20; }; struct Bar { int bari = 30; char barc = 'd'; }; int *iptr; // Plain old pointer-to-int Foo *fptr; // Plain old pointer-to-Foo (a custom class) ``````

but a pointer can be declared to point to a member of a specific class with some associated type:

 ``12345678`` ``````int Foo::* foomemptr; // Pointer to a Foo member (which must be an int) foomemptr = &Foo::fooi1; // Ok: foomemptr points to Foo::fooi1 member. The member is of a type that foomem expects: int foomemptr = &Foo::fooi2; // Ok: foomemptr points to (still) a Foo member of integer type foomemptr = &Bar::bari; // Error: a value of type "int Bar::*" cannot be assigned to an entity of type "int Foo::*" int Bar::* barmemptr; barmemptr = &Bar::bari; // Ok: Initializer is a pointer-to-Bar-member-that-is-int barmemptr = &Bar::barc; // Error: a value of type "char Bar::*" cannot be assigned to an entity of type "int Bar::*" ``````

So the `int Foo::*` part of the declarator for `foomemptr` means "a pointer that can only point to int found in the Foo class".

So just as we can dereference a "normal pointer":
 `` `` ``std::cout << *iptr << std::endl;``

We can also dereference a "pointer-to-specific-class-member" using the `.*` operator:

 ``12345678`` `````` Foo f1; // Let's instantiate a Foo object // Above, we've assigned foomemptr to point to the Foo::fooi2 member std::cout << f1.*foomemptr << std::endl; // Equivalent to: std::cout << f1.fooi2 << std::endl; // Let's reassign foomemptr so that it points to another Foo-int-member (Foo::fooi1) foomemptr = &Foo::fooi1; std::cout << f1.*foomemptr << std::endl; // Equivalent to: std::cout << f1.fooi1 << std::endl; ``````

But suppose instead of a `Foo` object, we started out with a pointer-to-Foo object. We can access fooi1 and fooi2 the same way by using the `->*` operator:

 ``1234567`` `````` Foo *f1ptr = &f1; // Recall: foomemptr still points to Foo:fooi1 at this point std::cout << f1ptr->*foomemptr << std::endl; // Equivalent to: std::out << f1.fooi1 << std::endl; foomemptr = &Foo::fooi2; std::cout << f1ptr->*foomemptr << std::endl; // Equivalent to: std::out << f1.fooi2 << std::endl; ``````

Full Example
 ``1234567891011121314151617181920212223242526272829303132333435363738394041`` ``````#include struct Foo { int fooi1 = 10; int fooi2 = 20; }; struct Bar { int bari = 30; char barc = 'd'; }; int main() { int Foo::* foomemptr; // Pointer to a Foo member (which must be an int) foomemptr = &Foo::fooi1; // Ok: foomemptr points to Foo::fooi1 member. The member is of a type that foomem expects: int foomemptr = &Foo::fooi2; // Ok: foomemptr points to (still) a Foo member of integer type //foomemptr = &Bar::bari; // Error: a value of type "int Bar::*" cannot be assigned to an entity of type "int Foo::*" int Bar::* barmemptr; barmemptr = &Bar::bari; // Ok: Initializer is a pointer-to-Bar-member-that-is-int //barmemptr = &Bar::barc; // Error: a value of type "char Bar::*" cannot be assigned to an entity of type "int Bar::*" Foo f1; // Let's instantiate a Foo object // Above, we've assigned foomemptr to point to the Foo::fooi2 member std::cout << f1.*foomemptr << std::endl; // "20" // Let's reassign foomemptr so that it points to another Foo-int-member (Foo::fooi1) foomemptr = &Foo::fooi1; std::cout << f1.*foomemptr << std::endl; // "10" // Say we started out with a pointer-to-Foo object instead. We can access fooi1 and fooi2 the same way by using the ->* operator Foo *f1ptr = &f1; // Recall: foomemptr still points to Foo:fooi1 at this point std::cout << f1ptr->*foomemptr << std::endl; // "10" foomemptr = &Foo::fooi2; std::cout << f1ptr->*foomemptr << std::endl; // "20" }``````
Last edited on
 What I meant to type, instead of *(*(lhs).rhs), was *((*lhs).rhs).

But that doesn't compile either, as I guess you've learned.
rhs cannot be used after a dot since it is not the name of a member but is only a kind of pseudo-pointer to a member of a certain type.
It is a new kind of thing and needs a new notation.
To use a member pointer you will always need either .* or ->*

I guess you can kind of simulate it like this:

 ``12`` `````` T o { 3, 4 }; std::cout << *(int*)((char*)&o + offsetof(T, y)) << '\n'; // print y member of o ``````

Interestingly,

 ``12345678910111213141516`` ``````#include int main() { struct T { int a, b, c, d; } o { 11, 22, 33, 44 }; union U { int T::* xp; size_t sz; } u; u.xp = &T::d; std::cout << u.sz << '\n'; // 12 std::cout << offsetof(T, d) << '\n'; // 12 }``````

Last edited on