93

Yes, I've seen this question and this FAQ, but I still don't understand what ->* and .* mean in C++.
Those pages provide information about the operators (such as overloading), but don't seem to explain well what they are.

What are ->* and .* in C++, and when do you need to use them as compared to -> and .?

user3840170
  • 26,597
  • 4
  • 30
  • 62
user541686
  • 205,094
  • 128
  • 528
  • 886

7 Answers7

104

I hope this example will clear things for you

//we have a class
struct X
{
   void f() {}
   void g() {}
};

typedef void (X::*pointer)();
//ok, let's take a pointer and assign f to it.
pointer somePointer = &X::f;
//now I want to call somePointer. But for that, I need an object
X x;
//now I call the member function on x like this
(x.*somePointer)(); //will call x.f()
//now, suppose x is not an object but a pointer to object
X* px = new X;
//I want to call the memfun pointer on px. I use ->*
(px ->* somePointer)(); //will call px->f();

Now, you can't use x.somePointer(), or px->somePointer() because there is no such member in class X. For that the special member function pointer call syntax is used... just try a few examples yourself ,you'll get used to it

Martin York
  • 257,169
  • 86
  • 333
  • 562
Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • 2
    Add the **required** extra braces to compensate for operator precedence. – Martin York Jul 05 '11 at 17:33
  • 1
    A pointer-to-member-function *may* be bigger than a regular pointer-to-function: specifically to cope with inheritance iirc – Useless Jul 05 '11 at 18:05
  • 2
    Think of it as a "relative" pointer: relative to an object -- which you'll then have to provide to actually reach the final destination. – Sz. Aug 09 '19 at 18:16
  • 1
    It's really good explanation and what are they. But I don't really why and when we would need such a thing though. – Random Jan 05 '22 at 23:15
  • Similar to @Sz. said, it (`&X::f`) is an "offset". See Sergey's comments on [this](https://stackoverflow.com/a/9468826/10027592) answer. And [this](https://stackoverflow.com/a/6586275/10027592) and [this](https://stackoverflow.com/a/6586401/10027592) answers also mention "offset". – starriet Jun 16 '23 at 11:53
28

EDIT: By the way, it gets weird for virtual member functions pointers.

For member variables:

struct Foo {
   int a;
   int b;
};


int main ()
{
    Foo foo;
    int (Foo :: * ptr);

    ptr = & Foo :: a;
    foo .*ptr = 123; // foo.a = 123;

    ptr = & Foo :: b;
    foo .*ptr = 234; // foo.b = 234;
}

Member functions are almost the same.

struct Foo {
   int a ();
   int b ();
};


int main ()
{
    Foo foo;
    int (Foo :: * ptr) ();

    ptr = & Foo :: a;
    (foo .*ptr) (); // foo.a ();

    ptr = & Foo :: b;
    (foo .*ptr) (); // foo.b ();
}
spraff
  • 32,570
  • 22
  • 121
  • 229
  • 6
    +1 for showing that the syntax applies to all members, not just member functions. I find that pointers to member variables are very rarely used, though, despite their many rather interesting potential applications. – Jon Purdy Jul 05 '11 at 17:18
17

In a nutshell: You use -> and . if you know what member you want to access. And you use ->* and .* if you don't know what member you want to access.

Example with a simple intrusive list

template<typename ItemType>
struct List {
  List(ItemType *head, ItemType * ItemType::*nextMemPointer)
  :m_head(head), m_nextMemPointer(nextMemPointer) { }

  void addHead(ItemType *item) {
    (item ->* m_nextMemPointer) = m_head;
    m_head = item;
  }

private:
  ItemType *m_head;

  // this stores the member pointer denoting the 
  // "next" pointer of an item
  ItemType * ItemType::*m_nextMemPointer;
};
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 9
    +1 for the first sentence, though never in my life have I *not* known which member I want to access, haha. :) – user541686 Jul 05 '11 at 17:45
11

So called "pointers" to members in C++ are more like offsets, internally. You need both such a member "pointer", and an object, to reference the member in the object. But member "pointers" are used with pointer syntax, hence the name.

There are two ways you can have an object at hand: you have a reference to the object, or you have a pointer to the object.

For the reference, use .* to combine it with a member pointer, and for the pointer, use ->* to combine it with a member pointer.

However, as a rule, don't use member pointers if you can avoid it.

They obey pretty counter-intuitive rules, and they make it possible to circumvent protected access without any explicit casting, that is, inadvertently…

Cheers & hth.,

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • 1
    +1 for explaining it well without code. :) Question: Why can't we just take the address of the function like that of a normal function? Is the pointer to a member function different from a pointer to some other function? (e.g. is it bigger?) – user541686 Jul 05 '11 at 17:44
  • 2
    @Mehrdad: If you could have a pointer to member function that was restricted to non-virtual member functions, then it could indeed just be the address. However, the virtual-ness or not is not part of the member function pointer type. And so its representation needs to include some information about whether the current value refers to a virtual function or not, and if virtual, for a vtable-based implementation information that determines an offset in the vtable of the class the pointer type is associated with. – Cheers and hth. - Alf Oct 21 '14 at 17:51
11

When you have a normal pointer (to an object or a basic type), you would use * to dereference it:

int a;
int* b = a;
*b = 5;     // we use *b to dereference b, to access the thing it points to

Conceptually, we're doing the same thing with a member function pointer:

class SomeClass
{
   public:  void func() {}
};

// typedefs make function pointers much easier.
// this is a pointer to a member function of SomeClass, which takes no parameters and returns void
typedef void (SomeClass::*memfunc)();

memfunc myPointer = &SomeClass::func;

SomeClass foo;

// to call func(), we could do:
foo.func();

// to call func() using our pointer, we need to dereference the pointer:
foo.*myPointer();
// this is conceptually just:    foo  .  *myPointer  ();


// likewise with a pointer to the object itself:
SomeClass* p = new SomeClass;

// normal call func()
p->func();

// calling func() by dereferencing our pointer:
p->*myPointer();
// this is conceptually just:    p  ->  *myPointer  ();

I hope that helps explain the concept. We're effectively dereferencing our pointer to the member function. It's a little more complicated than that -- it's not an absolute pointer to a function in memory, but just an offset, which is applied to foo or p above. But conceptually, we're dereferencing it, much like we would dereference a normal object pointer.

Tim
  • 8,912
  • 3
  • 39
  • 57
  • 2
    Trying to compile gives `must use '.*' or '->*' to call pointer-to-member function...` error. This is due to the function call `()` taking precedence over the member-by-pointer operator. I think this can be fixed by adding parenthesis `(foo.*myPointer)();` and `(p->*myPointer)();`, respectively. – dgruending Aug 12 '21 at 16:01
6

Pointer-to-member access operators: .* and ->*

The pointer-to-member access operators, .* and ->*, are for dereferencing a pointer to member in combination with an object and a pointer to object, respectively. This description applies to both pointers to data members and pointers to member functions.

For example, consider the class Foo:

struct Foo {
   int i;
   void f();
};

If you declare a member pointer, iPtr, to an int data member of Foo:

int Foo::* iPtr;

You can initialize this member pointer iPtr so that it points to the Foo::i member:

iPtr = &Foo::i;

To dereference this pointer, you need to use it in conjunction with a Foo object.

Consider now the object foo and the pointer to object fooPtr:

Foo foo;
Foo* fooPtr = &foo;

Then, you can dereference iPtr in combination with foo or fooPtr:

foo.*iPtr = 0;
fooPtr->*iPtr = 0;

Analogously, you can use .* and ->* with pointers to function members. Note however that you will need to enclose them between parentheses because the function call operator, i.e., (), has higher precedence than both .* and ->*:

void (Foo::*memFuncPtr)() = &Foo::f;

(foo.*memFuncPtr)();
(fooPtr->*memFuncPtr)();

To conclude: you need an object to dereference a pointer to a member, and which one you use, either .* or ->* for dereferencing the pointer to member, depends on whether this needed object is directly provided or through an object pointer.

C++17 — Using std::invoke() instead

The use of both operators can be replaced since C++17 by the std::invoke function template. std::invoke provides a unified way of dereferencing member pointers regardless of whether you use them in combination with an object or an object pointer, and also regardless of whether the pointer to member corresponds to a pointer to data member or pointer to member function:

// dereference a pointer to a data member
std::invoke(iPtr, foo) = 0;      // with an object
std::invoke(iPtr, fooPtr) = 0;   // with an object pointer

// dereference a pointer to a member function
std::invoke(memFuncPtr, foo);      // with an object
std::invoke(memFuncPtr, fooPtr);   // with an object pointer

This unified syntax corresponds to the ordinary function call syntax, and it may make it easier to write generic code.

JFMR
  • 23,265
  • 4
  • 52
  • 76
6

You cannot dereference pointer to members as normal pointers — because member functions require this pointer, and you have to pass it somehow. So, you need to use these two operators, with object on one side, and pointer on another, e.g. (object.*ptr)().

Consider using function and bind (std:: or boost::, depending on whether you write C++03 or 0x) instead of those, though.

Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224