155

I understand most operator overloading, with the exception of the member access operators ->, .*, ->* etc.

In particular, what is passed to these operator functions, and what should be returned?

How does the operator function (e.g. operator->(...) ) know what member is being refered to? Can it know? Does it even need to know?

Finally, are there any const considerations that need to be taken into account? For example, when overloading something like operator[], generally you will need both a const and non-const version. Do member access operators require const and non-const versions?

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55
Bingo
  • 3,785
  • 6
  • 23
  • 27
  • 1
    I believe the above C++-Faq touches upon all the Q's asked in the above Q. – Alok Save Jan 08 '12 at 13:19
  • `const` and non-`const` versions of `operator->` aren't *required*, but providing both can be useful. – Fred Foo Jan 08 '12 at 13:26
  • 1
    See also: http://yosefk.com/c++fqa/operator.html – György Andrasek Jan 08 '12 at 13:42
  • 9
    @Als: The FAQ does not explain how to overload `->*` and `.*`. In fact, it doesn't even mention them! I feel they are to rare to be in an FAQ, but I'd gladly link this question from the FAQ. ___Please do not close this as a dupe of the FAQ!___ – sbi Jan 19 '12 at 11:56
  • 1
    @sbi, I just completely failed to find a link to this question from your (awesome) FAQ, and ended up asking a duplicate question. Could you make it more obvious? (apologies if it is already obvious). – P i Dec 29 '14 at 14:18

5 Answers5

188

->

This is the only really tricky one. It must be a nonstatic member function, and it takes no arguments. The return value is used to perform the member lookup.

If the return value is another object of class type, not a pointer, then the subsequent member lookup is also handled by an operator-> function. This is called the "drill-down behavior." The language chains together the operator-> calls until the last one returns a pointer.

struct client
    { int a; };

struct proxy {
    client *target;
    client *operator->() const
        { return target; }
};

struct proxy2 {
    proxy *target;
    proxy &operator->() const
        { return * target; }
};

void f() {
    client x = { 3 };
    proxy y = { & x };
    proxy2 z = { & y };

    std::cout << x.a << y->a << z->a; // print "333"
}

->*

This one is only tricky in that there is nothing special about it. The non-overloaded version requires an object of pointer to class type on the left-hand side and an object of pointer to member type on the right. But when you overload it, you can take any arguments you like and return anything you want. It doesn't even have to be a nonstatic member.

In other words, this one is just a normal binary operator like +, -, and /. See also: Are free operator->* overloads evil?

.* and .

These cannot be overloaded. There is already a built-in meaning when the left-hand side is of class type. Perhaps it would make a little sense to be able to define them for a pointer on the left-hand side, but the language design committee decided that would be more confusing than useful.

Overloading ->, ->*, ., and .* can only fill in cases where an expression would be undefined, it can never change the meaning of an expression that would be valid with no overloading.

Community
  • 1
  • 1
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • 2
    Your last statement is not completely true. For example, you can overload the `new` operator, even though it is valid even when not overloaded. – Matt Feb 17 '13 at 04:49
  • 6
    @Matt well, `new` is always overloaded, or overloading rules don't really apply to it (13.5/5: The allocation and deallocation functions, operator new, operator new[], operator delete and operator delete[], are described completely in 3.7.4. The attributes and restrictions found in the rest of this subclause do not apply to them unless explicitly stated in 3.7.4.) But overloading unary `&` or binary `&&`, `||`, or `,`, or adding overloads of `operator=`, or overloading just about anything for an unscoped enumeration type, can change an expression's meaning. Clarified the statement, thanks! – Potatoswatter Feb 18 '13 at 01:05
  • I don't understand what does "object of pointer to class type" mean, exactly? – Armen Michaeli Mar 24 '23 at 23:15
57

Operator -> is special.

"It has additional, atypical constraints: It must return an object (or reference to an object) that also has a pointer dereference operator, or it must return a pointer that can be used to select what the pointer dereference operator arrow is pointing at." Bruce Eckel: Thinking CPP Vol-one : operator->

The extra functionality is provided for convenience, so you do not have to call

a->->func();

You can simply do:

a->func();

That makes operator -> different from the other operator overloads.

Totonga
  • 4,236
  • 2
  • 25
  • 31
  • 4
    This answer deserves more credit, you can download Eckel's book from that link and the information is in chapter 12 of volume one. – P i Dec 29 '14 at 14:36
  • Or look at the section 13.5.6 of the C++11 standard. You can read the free draft version at [N3242](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf#subsection.13.5.6). – Lei Zhao Aug 30 '22 at 19:35
29

You cannot overload member access . (i.e. the second part of what -> does). You can however overload the unary dereferencing operator * (i.e. the first part of what -> does).

The C++ -> operator is basically the union of two steps and this is clear if you think that x->y is equivalent to (*x).y. C++ allows you to customize what to do with the (*x) part when x is an instance of your class.

The semantic for -> overloading is somewhat strange because C++ allows you either to return a regular pointer (that it will be used to find the pointed object) or to return an instance of another class if this class also provides a -> operator. When in this second case the search for the dereferenced object continues from this new instance.

Bingo
  • 3,785
  • 6
  • 23
  • 27
6502
  • 112,025
  • 15
  • 165
  • 265
  • 3
    Great explanation! I guess that means the same for `->*`, as it is equivalent to the form of `(*x).*` ? – Bingo Jan 08 '12 at 23:34
10

The -> operator doesn't know what member is being pointed to, it just provides an object to perform the actual member access on.

Additionally, I see no reason why you can't provide const and non-const versions.

John Chadwick
  • 3,193
  • 19
  • 19
7

When you overload operator->() (no arguments are passed here), what the compiler actually does is calling -> recursively until it returns an actual pointer to a type. It then uses the correct member/method.

This is useful, for example, to create a smart pointer class which encapsulates the actual pointer. The overloaded operator-> is called, does whatever it does (e.g. locking for thread safety), returns the internal pointer and then the compiler calls -> for this internal pointer.

As for constness - it's been answered in the comments and other answers (you can, and should, provide both).

Asaf
  • 4,317
  • 28
  • 48