9

It is said that the arrow operator is applied recursively. But when I try to execute the following code, it prints gibberish when it is supposed to print 4.

class dummy
{
public:
    int *p;

    int operator->()
    {
        return 4;
    }
};

class screen 
{
public:
    dummy *p;

    screen(dummy *pp): p(pp){}
    dummy* operator->()
    {
        return p;
    }
};

int main()
{
    dummy *d = new dummy;
    screen s(d);
    cout<<s->p;
    delete d;
}
Shahbaz
  • 46,337
  • 19
  • 116
  • 182
user1232138
  • 5,451
  • 8
  • 37
  • 64
  • 6
    Where is it said that it's "applied recursively"? – Matti Virkkunen May 05 '12 at 09:49
  • Nope I disagree your example works as expected, the -> opertor is just a function call in essence, why should it drill down? If it it do that how would you control at what level to stop dereferencing and it would make inheritance and polymorphism even more complicated than it already is – EdChum May 05 '12 at 09:52
  • 3
    C++ Primer, Fourth Edition By Stanley B. Lippman , section 14.6 last paragraph. – user1232138 May 05 '12 at 09:52

3 Answers3

15

What Stanley meant by “recursive” is just that the operator is applied to every returned object until the returned type is a pointer.

Which happens here on the first try: screen::operator -> returns a pointer. Thus this is the last call to an operator -> that the compiler attempts. It then resolves the right-hand sice of the operator (p) by looking up a member in the returned pointee type (dummy) with that name.

Essentially, whenever the compiler finds the syntax aᵢ->b in code, it essentially applies the following algorithm:

  1. Is aᵢ of pointer type? If so, resolve member b of *aᵢ and call (*aᵢ).b.
  2. Else, try to resolve aᵢ::operator ->
    1. On success, set aᵢ₊₁ = aᵢ::operator ->(). Goto 1.
    2. On failure, emit a compile error.

I’m hard-pressed to come up with a short, meaningful example where a chain of operator -> invocations even makes sense. Probably the only real use is when you write a smart pointer class.

However, the following toy example at least compiles and yields a number. But I wouldn’t advise actually writing such code. It breaks encapsulation and makes kittens cry.

#include <iostream>

struct size {
    int width;
    int height;
    size() : width(640), height(480) { }
};

struct metrics {
    size s;
    size const* operator ->() const {
        return &s;
    }
};

struct screen {
    metrics m;
    metrics operator ->() const {
        return m;
    }
};

int main() {
    screen s;
    std::cout << s->width << "\n";
}
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • maybe my concept would be more clear if you could tell me how I can get 4 to be printed out in this example.. – user1232138 May 05 '12 at 10:07
  • @user1232138 You can’t. The last call in the chain of `operator ->` **must** return a pointer. – Konrad Rudolph May 05 '12 at 10:13
  • @user1232138 However, see my update for a working example. Note that what I said before still holds. You must return a pointer – so if you want to retrieve an integer value, it needs to be stored in the right-hand side expression of the operator (in this case, `size::width`). – Konrad Rudolph May 05 '12 at 10:27
  • Does `operator->*` obey the same rules ? – Alexandre C. May 05 '12 at 10:37
  • @AlexandreC. I don’t know. I have no idea how `operator ->*` actually works – for instance, Catch.hpp uses it to get a *string representation* of the subsequent expression. F*cking magic, as far as I’m concerned. – Konrad Rudolph May 05 '12 at 11:12
2

C++ Primer (5th edition) formulates it as follows on page 570:

The arrow operator never loses its fundamental meaning of member access. When we overload arrow, we change the object from which arrow fetches the specified member. We cannot change the fact that arrow fetches a member.

AlwaysLearning
  • 7,257
  • 4
  • 33
  • 68
1

The deal is once screen::operator->() returns a pointer (dummy*) the recursion stops because built-in (default) -> in used on that pointer. If you want recursion you should return dummy or dummy& from screen::operator->()

sharptooth
  • 167,383
  • 100
  • 513
  • 979