Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

overload "->" (member access) recursively

Tags:

c++

I am learning how to overload "->" and the documentation says that: "operator-> is called again on the value that it returns, recursively, until the operator-> is reached that returns a plain pointer. After that, builtin semantics are applied to that pointer."

While it is clear what the documentation says, essentially that an overloaded "->" of a class could use itself a "special pointer" having itself an overloaded "->" that could give a "special pointer" etc etc until a "plain pointer" is found, I cannot find an example of a real use of it ( unless it is used to find a linked list last element ).

Could somebody explain what is the retionale behind the scenes, ( as that possibility isn't provided with "plain pointers" - so I dont' see any reason to provide it with "special pointers" ).

An example of real world use could help too, as probably I am missing a model where to apply the behaviour.

On the opposite side there could be the need to avoid that behaviour, how could it be done ?

like image 293
George Kourtis Avatar asked Jan 09 '23 22:01

George Kourtis


2 Answers

Well, the -> operator works under rater special circumstances.

One can call it a pseudo-binary operator. According to its natural syntax pointer->member it takes two operands: a normal run-time operand on the left-hand side and a rather "strange" member name operand on the right-hand side. The "strangeness" of the second operand is rooted in the fact that C++ language has no user-accessible concept for representing such operands. There's nothing in the language that would express a member name as an operand. There's no way to "pass" a member name through the code to the user-defined implementation. The member name is a compile-time entity, remotely similar to constant expressions in that regard, but no constant expression in C++ can specify members. (There are expressions for pointers-to-members, but not for members themselves).

This creates rather obvious difficulties in specifying the behavior of overloaded -> operator: how do we connect what was specified on the right-hand side of -> (i.e the member name) to the code written by the user? It is not possible to do it directly. The only way out of this situation is to do it indirectly: force the user to channel the user-defined functionality of the overloaded -> operator into the functionality of some existing built-in operator. The built-in operator can handle member names naturally, through its core language capabilities.

In this particular case we have only two candidates to channel the functionality of the overloaded -> to: the built-in -> and the built-in .. It is only logical that the built-in -> was chosen for that role. This created an interesting side-effect: the possibility to write "chained" (recursive) sequences of overloaded -> operators (unwrapped implicitly by the compiler) and even infinitely recursive sequences (which are ill-formed).

Informally speaking, every time you use a smart pointer you make a real-world use of these "recursive" properties of overloaded -> operator. If you have a smart pointer sptr that points to a class object with member member, the member access syntax remains perfectly natural, e.g. sptr->member. You don't have to do it as sptr->->member or sptr->.member specifically because of the implicit "recursive" properties of overloaded ->.

Note that this recursive behavior is only applied when you use operator syntax for invoking the overloaded -> operator, i.e. the object->member syntax. However, you can also use the regular member function call syntax to call your overloaded ->, e.g. object.operator ->(). In this case the call is carried out as an ordinary function call and no recursive application of -> takes place. This is the only way to avoid the recursive behavior. If you implement overloaded -> operator whose return type does not support further applications of -> operator (for example, you can define an overloaded -> that returns int), then the object.operator ->() will be the only way to invoke your overloaded implementation. Any attempts to use the object->member syntax will be ill-formed.

like image 126
AnT Avatar answered Jan 18 '23 22:01

AnT


I cannot find an example of a real use of it ( unless it is used to find a linked list last element ).

I think you're misunderstanding what it does. It isn't used to dereference a list element and keep dereferencing the next element. Each time you call operator-> you would get back a different type, the point is that if that second type also has an operator-> it will be called, which might return a different type again. Imagine it being like x->->->i not x->next->next->next if that helps

An example of real world use could help too, as probably I am missing a model where to apply the behaviour.

It can be useful for the Execute Around Pointer pattern.

On the opposite side there could be the need to avoid that behaviour, how could it be done ?

Call the operator explicitly:

auto x = p.operator->();
like image 45
Jonathan Wakely Avatar answered Jan 18 '23 22:01

Jonathan Wakely